@backstage/plugin-scaffolder 1.10.0-next.0 → 1.10.0-next.2

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.
@@ -1,32 +1,40 @@
1
- import { parseEntityRef, KubernetesValidatorFunctions, makeValidator, RELATION_OWNED_BY } from '@backstage/catalog-model';
2
- import { createApiRef, useApi, identityApiRef, attachComponentData, createExternalRouteRef, createRouteRef, createSubRouteRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, alertApiRef, useApp, useRouteRef, useRouteRefParams } from '@backstage/core-plugin-api';
1
+ import { parseEntityRef, KubernetesValidatorFunctions, RELATION_OWNED_BY, makeValidator, stringifyEntityRef } from '@backstage/catalog-model';
3
2
  import { ResponseError } from '@backstage/errors';
4
3
  import qs from 'qs';
5
4
  import ObservableImpl from 'zen-observable';
5
+ import { attachComponentData, useApi, identityApiRef, createExternalRouteRef, createRouteRef, createSubRouteRef, useRouteRef, useRouteRefParams, errorApiRef, AnalyticsContext, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, alertApiRef, useApp } from '@backstage/core-plugin-api';
6
+ import { scmIntegrationsApiRef, scmAuthApiRef } from '@backstage/integration-react';
7
+ import { scaffolderApiRef as scaffolderApiRef$1, useTemplateSecrets as useTemplateSecrets$1, Stepper, createScaffolderFieldExtension as createScaffolderFieldExtension$1, ScaffolderFieldExtensions as ScaffolderFieldExtensions$1 } from '@backstage/plugin-scaffolder-react';
6
8
  import { catalogApiRef, humanizeEntityRef, useEntityTypeFilter, entityRouteRef } from '@backstage/plugin-catalog-react';
7
- import { TextField, FormControl as FormControl$1, Box, Typography, FormControlLabel, Checkbox, makeStyles, Grid, StepButton, Paper, Button, CircularProgress } from '@material-ui/core';
9
+ import { TextField, FormControl as FormControl$1, makeStyles, Box, Typography, FormControlLabel, Checkbox, Grid, StepButton, Paper, Button, CircularProgress } from '@material-ui/core';
8
10
  import FormControl from '@material-ui/core/FormControl';
9
11
  import Autocomplete from '@material-ui/lab/Autocomplete';
10
- import React, { useCallback, useEffect, useState, createContext, useContext, useMemo, useRef, memo } from 'react';
12
+ import React, { useCallback, useEffect, useState, useMemo, useRef, memo } from 'react';
11
13
  import useAsync from 'react-use/lib/useAsync';
12
14
  import { z } from 'zod';
13
15
  import zodToJsonSchema from 'zod-to-json-schema';
14
- import useEffectOnce from 'react-use/lib/useEffectOnce';
15
- import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
16
- import { scmIntegrationsApiRef, scmAuthApiRef } from '@backstage/integration-react';
17
16
  import FormHelperText from '@material-ui/core/FormHelperText';
18
17
  import Input from '@material-ui/core/Input';
19
18
  import InputLabel from '@material-ui/core/InputLabel';
20
- import { Select, Progress, DismissableBanner, Link, Page, Header, Content, ErrorPage, LogViewer } from '@backstage/core-components';
19
+ import { Select, Progress, Page, Header, Content, InfoCard, MarkdownContent, DismissableBanner, Link, ErrorPage, LogViewer } from '@backstage/core-components';
21
20
  import useDebounce from 'react-use/lib/useDebounce';
21
+ import useEffectOnce from 'react-use/lib/useEffectOnce';
22
+ import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
23
+ import { useNavigate, Navigate } from 'react-router-dom';
22
24
  import capitalize from 'lodash/capitalize';
23
25
  import CheckBoxIcon from '@material-ui/icons/CheckBox';
24
26
  import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
25
27
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
28
+ import '@material-ui/core/Button';
29
+ import '@material-ui/core/IconButton';
30
+ import '@material-ui/core/useMediaQuery';
31
+ import '@material-ui/icons/AddCircleOutline';
32
+ import '@backstage/plugin-catalog-common';
33
+ import '@backstage/plugin-permission-react';
26
34
  import Grid$1 from '@material-ui/core/Grid';
27
35
  import Step from '@material-ui/core/Step';
28
36
  import StepLabel from '@material-ui/core/StepLabel';
29
- import Stepper from '@material-ui/core/Stepper';
37
+ import Stepper$1 from '@material-ui/core/Stepper';
30
38
  import { makeStyles as makeStyles$1, createStyles } from '@material-ui/core/styles';
31
39
  import Typography$1 from '@material-ui/core/Typography';
32
40
  import Cancel from '@material-ui/icons/Cancel';
@@ -34,14 +42,10 @@ import Check from '@material-ui/icons/Check';
34
42
  import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
35
43
  import classNames from 'classnames';
36
44
  import { DateTime, Interval } from 'luxon';
37
- import { useNavigate } from 'react-router-dom';
38
45
  import useInterval from 'react-use/lib/useInterval';
39
46
  import { useImmerReducer } from 'use-immer';
40
47
  import LanguageIcon from '@material-ui/icons/Language';
41
48
 
42
- const scaffolderApiRef = createApiRef({
43
- id: "plugin.scaffolder.service"
44
- });
45
49
  class ScaffolderClient {
46
50
  constructor(options) {
47
51
  var _a, _b;
@@ -238,6 +242,20 @@ class ScaffolderClient {
238
242
  }
239
243
  }
240
244
 
245
+ const LAYOUTS_KEY = "scaffolder.layout.v1";
246
+ const LAYOUTS_WRAPPER_KEY = "scaffolder.layouts.wrapper.v1";
247
+ function createScaffolderLayout(options) {
248
+ return {
249
+ expose() {
250
+ const LayoutDataHolder = () => null;
251
+ attachComponentData(LayoutDataHolder, LAYOUTS_KEY, options);
252
+ return LayoutDataHolder;
253
+ }
254
+ };
255
+ }
256
+ const ScaffolderLayouts = () => null;
257
+ attachComponentData(ScaffolderLayouts, LAYOUTS_WRAPPER_KEY, true);
258
+
241
259
  function makeFieldSchemaFromZod(returnSchema, uiOptionsSchema) {
242
260
  return {
243
261
  schema: {
@@ -255,6 +273,9 @@ const entityQueryFilterExpressionSchema = z.record(
255
273
  const EntityPickerFieldSchema = makeFieldSchemaFromZod(
256
274
  z.string(),
257
275
  z.object({
276
+ /**
277
+ * @deprecated Use `catalogFilter` instead.
278
+ */
258
279
  allowedKinds: z.array(z.string()).optional().describe(
259
280
  "DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from"
260
281
  ),
@@ -341,6 +362,14 @@ const EntityPicker = (props) => {
341
362
  );
342
363
  };
343
364
 
365
+ const entityNamePickerValidation = (value, validation) => {
366
+ if (!KubernetesValidatorFunctions.isValidObjectName(value)) {
367
+ validation.addError(
368
+ "Must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters."
369
+ );
370
+ }
371
+ };
372
+
344
373
  const EntityNamePickerFieldSchema = makeFieldSchemaFromZod(z.string());
345
374
  const EntityNamePickerSchema = EntityNamePickerFieldSchema.schema;
346
375
 
@@ -372,98 +401,12 @@ const EntityNamePicker = (props) => {
372
401
  );
373
402
  };
374
403
 
375
- const entityNamePickerValidation = (value, validation) => {
376
- if (!KubernetesValidatorFunctions.isValidObjectName(value)) {
377
- validation.addError(
378
- "Must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters."
379
- );
380
- }
381
- };
382
-
383
- const EntityTagsPickerFieldSchema = makeFieldSchemaFromZod(
384
- z.array(z.string()),
385
- z.object({
386
- kinds: z.array(z.string()).optional().describe("List of kinds of entities to derive tags from"),
387
- showCounts: z.boolean().optional().describe("Whether to show usage counts per tag"),
388
- helperText: z.string().optional().describe("Helper text to display")
389
- })
390
- );
391
- const EntityTagsPickerSchema = EntityTagsPickerFieldSchema.schema;
392
-
393
- const EntityTagsPicker = (props) => {
394
- var _a, _b, _c;
395
- const { formData, onChange, uiSchema } = props;
396
- const catalogApi = useApi(catalogApiRef);
397
- const [tagOptions, setTagOptions] = useState([]);
398
- const [inputValue, setInputValue] = useState("");
399
- const [inputError, setInputError] = useState(false);
400
- const tagValidator = makeValidator().isValidTag;
401
- const kinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.kinds;
402
- const showCounts = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.showCounts;
403
- const helperText = (_c = uiSchema["ui:options"]) == null ? void 0 : _c.helperText;
404
- const { loading, value: existingTags } = useAsync(async () => {
405
- const facet = "metadata.tags";
406
- const tagsRequest = { facets: [facet] };
407
- if (kinds) {
408
- tagsRequest.filter = { kind: kinds };
409
- }
410
- const { facets } = await catalogApi.getEntityFacets(tagsRequest);
411
- const tagFacets = Object.fromEntries(
412
- facets[facet].map(({ value, count }) => [value, count])
413
- );
414
- setTagOptions(
415
- Object.keys(tagFacets).sort(
416
- (a, b) => showCounts ? tagFacets[b] - tagFacets[a] : a.localeCompare(b)
417
- )
418
- );
419
- return tagFacets;
420
- });
421
- const setTags = (_, values) => {
422
- let hasError = false;
423
- let addDuplicate = false;
424
- const currentTags = formData || [];
425
- if ((values == null ? void 0 : values.length) && currentTags.length < values.length) {
426
- const newTag = values[values.length - 1] = values[values.length - 1].toLocaleLowerCase("en-US").trim();
427
- hasError = !tagValidator(newTag);
428
- addDuplicate = currentTags.indexOf(newTag) !== -1;
429
- }
430
- setInputError(hasError);
431
- setInputValue(!hasError ? "" : inputValue);
432
- if (!hasError && !addDuplicate) {
433
- onChange(values || []);
434
- }
435
- };
436
- useEffectOnce(() => onChange(formData || []));
437
- return /* @__PURE__ */ React.createElement(FormControl$1, { margin: "normal" }, /* @__PURE__ */ React.createElement(
438
- Autocomplete$1,
439
- {
440
- multiple: true,
441
- freeSolo: true,
442
- filterSelectedOptions: true,
443
- onChange: setTags,
444
- value: formData || [],
445
- inputValue,
446
- loading,
447
- options: tagOptions,
448
- ChipProps: { size: "small" },
449
- renderOption: (option) => showCounts ? `${option} (${existingTags == null ? void 0 : existingTags[option]})` : option,
450
- renderInput: (params) => /* @__PURE__ */ React.createElement(
451
- TextField,
452
- {
453
- ...params,
454
- label: "Tags",
455
- onChange: (e) => setInputValue(e.target.value),
456
- error: inputError,
457
- helperText: helperText != null ? helperText : "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters"
458
- }
459
- )
460
- }
461
- ));
462
- };
463
-
464
404
  const OwnerPickerFieldSchema = makeFieldSchemaFromZod(
465
405
  z.string(),
466
406
  z.object({
407
+ /**
408
+ * @deprecated Use `catalogFilter` instead.
409
+ */
467
410
  allowedKinds: z.array(z.string()).default(["Group", "User"]).optional().describe(
468
411
  "DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from. Defaults to Group and User"
469
412
  ),
@@ -507,6 +450,71 @@ const OwnerPicker = (props) => {
507
450
  );
508
451
  };
509
452
 
453
+ const RepoUrlPickerFieldSchema = makeFieldSchemaFromZod(
454
+ z.string(),
455
+ z.object({
456
+ allowedHosts: z.array(z.string()).optional().describe("List of allowed SCM platform hosts"),
457
+ allowedOrganizations: z.array(z.string()).optional().describe("List of allowed organizations in the given SCM platform"),
458
+ allowedOwners: z.array(z.string()).optional().describe("List of allowed owners in the given SCM platform"),
459
+ allowedProjects: z.array(z.string()).optional().describe("List of allowed projects in the given SCM platform"),
460
+ allowedRepos: z.array(z.string()).optional().describe("List of allowed repos in the given SCM platform"),
461
+ requestUserCredentials: z.object({
462
+ secretsKey: z.string().describe(
463
+ "Key used within the template secrets context to store the credential"
464
+ ),
465
+ additionalScopes: z.object({
466
+ gerrit: z.array(z.string()).optional().describe("Additional Gerrit scopes to request"),
467
+ github: z.array(z.string()).optional().describe("Additional GitHub scopes to request"),
468
+ gitlab: z.array(z.string()).optional().describe("Additional GitLab scopes to request"),
469
+ bitbucket: z.array(z.string()).optional().describe("Additional BitBucket scopes to request"),
470
+ azure: z.array(z.string()).optional().describe("Additional Azure scopes to request")
471
+ }).optional().describe("Additional permission scopes to request")
472
+ }).optional().describe(
473
+ "If defined will request user credentials to auth against the given SCM platform"
474
+ )
475
+ })
476
+ );
477
+ const RepoUrlPickerSchema = RepoUrlPickerFieldSchema.schema;
478
+
479
+ const repoPickerValidation = (value, validation, context) => {
480
+ var _a, _b;
481
+ try {
482
+ const { host, searchParams } = new URL(`https://${value}`);
483
+ const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);
484
+ if (!host) {
485
+ validation.addError(
486
+ "Incomplete repository location provided, host not provided"
487
+ );
488
+ } else {
489
+ if (((_a = integrationApi == null ? void 0 : integrationApi.byHost(host)) == null ? void 0 : _a.type) === "bitbucket") {
490
+ if (host === "bitbucket.org" && !searchParams.get("workspace")) {
491
+ validation.addError(
492
+ "Incomplete repository location provided, workspace not provided"
493
+ );
494
+ }
495
+ if (!searchParams.get("project")) {
496
+ validation.addError(
497
+ "Incomplete repository location provided, project not provided"
498
+ );
499
+ }
500
+ } else if (((_b = integrationApi == null ? void 0 : integrationApi.byHost(host)) == null ? void 0 : _b.type) !== "gerrit") {
501
+ if (!searchParams.get("owner")) {
502
+ validation.addError(
503
+ "Incomplete repository location provided, owner not provided"
504
+ );
505
+ }
506
+ }
507
+ if (!searchParams.get("repo")) {
508
+ validation.addError(
509
+ "Incomplete repository location provided, repo not provided"
510
+ );
511
+ }
512
+ }
513
+ } catch {
514
+ validation.addError("Unable to parse the Repository URL");
515
+ }
516
+ };
517
+
510
518
  const GithubRepoPicker = (props) => {
511
519
  const { allowedOwners = [], rawErrors, state, onChange } = props;
512
520
  const ownerItems = allowedOwners ? allowedOwners.map((i) => ({ label: i, value: i })) : [{ label: "Loading...", value: "loading" }];
@@ -744,7 +752,7 @@ const GerritRepoPicker = (props) => {
744
752
 
745
753
  const RepoUrlPickerHost = (props) => {
746
754
  const { host, hosts, onChange, rawErrors } = props;
747
- const scaffolderApi = useApi(scaffolderApiRef);
755
+ const scaffolderApi = useApi(scaffolderApiRef$1);
748
756
  const { value: { integrations } = { integrations: [] }, loading } = useAsync(
749
757
  async () => {
750
758
  return await scaffolderApi.getIntegrationsList({
@@ -871,56 +879,6 @@ function parseRepoPickerUrl(url) {
871
879
  return { host, owner, repoName, organization, workspace, project };
872
880
  }
873
881
 
874
- const SecretsContext = createContext(
875
- void 0
876
- );
877
- const SecretsContextProvider = ({ children }) => {
878
- const [secrets, setSecrets] = useState({});
879
- return /* @__PURE__ */ React.createElement(SecretsContext.Provider, { value: { secrets, setSecrets } }, children);
880
- };
881
- const useTemplateSecrets = () => {
882
- const value = useContext(SecretsContext);
883
- if (!value) {
884
- throw new Error(
885
- "useTemplateSecrets must be used within a SecretsContextProvider"
886
- );
887
- }
888
- const { setSecrets: updateSecrets } = value;
889
- const setSecrets = useCallback(
890
- (input) => {
891
- updateSecrets((currentSecrets) => ({ ...currentSecrets, ...input }));
892
- },
893
- [updateSecrets]
894
- );
895
- return { setSecrets };
896
- };
897
-
898
- const RepoUrlPickerFieldSchema = makeFieldSchemaFromZod(
899
- z.string(),
900
- z.object({
901
- allowedHosts: z.array(z.string()).optional().describe("List of allowed SCM platform hosts"),
902
- allowedOrganizations: z.array(z.string()).optional().describe("List of allowed organizations in the given SCM platform"),
903
- allowedOwners: z.array(z.string()).optional().describe("List of allowed owners in the given SCM platform"),
904
- allowedProjects: z.array(z.string()).optional().describe("List of allowed projects in the given SCM platform"),
905
- allowedRepos: z.array(z.string()).optional().describe("List of allowed repos in the given SCM platform"),
906
- requestUserCredentials: z.object({
907
- secretsKey: z.string().describe(
908
- "Key used within the template secrets context to store the credential"
909
- ),
910
- additionalScopes: z.object({
911
- gerrit: z.array(z.string()).optional().describe("Additional Gerrit scopes to request"),
912
- github: z.array(z.string()).optional().describe("Additional GitHub scopes to request"),
913
- gitlab: z.array(z.string()).optional().describe("Additional GitLab scopes to request"),
914
- bitbucket: z.array(z.string()).optional().describe("Additional BitBucket scopes to request"),
915
- azure: z.array(z.string()).optional().describe("Additional Azure scopes to request")
916
- }).optional().describe("Additional permission scopes to request")
917
- }).optional().describe(
918
- "If defined will request user credentials to auth against the given SCM platform"
919
- )
920
- })
921
- );
922
- const RepoUrlPickerSchema = RepoUrlPickerFieldSchema.schema;
923
-
924
882
  const RepoUrlPicker = (props) => {
925
883
  var _a, _b;
926
884
  const { uiSchema, onChange, rawErrors, formData } = props;
@@ -929,7 +887,7 @@ const RepoUrlPicker = (props) => {
929
887
  );
930
888
  const integrationApi = useApi(scmIntegrationsApiRef);
931
889
  const scmAuthApi = useApi(scmAuthApiRef);
932
- const { setSecrets } = useTemplateSecrets();
890
+ const { setSecrets } = useTemplateSecrets$1();
933
891
  const allowedHosts = useMemo(
934
892
  () => {
935
893
  var _a2, _b2;
@@ -1089,45 +1047,6 @@ const RepoUrlPicker = (props) => {
1089
1047
  ));
1090
1048
  };
1091
1049
 
1092
- const repoPickerValidation = (value, validation, context) => {
1093
- var _a, _b;
1094
- try {
1095
- const { host, searchParams } = new URL(`https://${value}`);
1096
- const integrationApi = context.apiHolder.get(scmIntegrationsApiRef);
1097
- if (!host) {
1098
- validation.addError(
1099
- "Incomplete repository location provided, host not provided"
1100
- );
1101
- } else {
1102
- if (((_a = integrationApi == null ? void 0 : integrationApi.byHost(host)) == null ? void 0 : _a.type) === "bitbucket") {
1103
- if (host === "bitbucket.org" && !searchParams.get("workspace")) {
1104
- validation.addError(
1105
- "Incomplete repository location provided, workspace not provided"
1106
- );
1107
- }
1108
- if (!searchParams.get("project")) {
1109
- validation.addError(
1110
- "Incomplete repository location provided, project not provided"
1111
- );
1112
- }
1113
- } else if (((_b = integrationApi == null ? void 0 : integrationApi.byHost(host)) == null ? void 0 : _b.type) !== "gerrit") {
1114
- if (!searchParams.get("owner")) {
1115
- validation.addError(
1116
- "Incomplete repository location provided, owner not provided"
1117
- );
1118
- }
1119
- }
1120
- if (!searchParams.get("repo")) {
1121
- validation.addError(
1122
- "Incomplete repository location provided, repo not provided"
1123
- );
1124
- }
1125
- }
1126
- } catch {
1127
- validation.addError("Unable to parse the Repository URL");
1128
- }
1129
- };
1130
-
1131
1050
  const OwnedEntityPickerFieldSchema = makeFieldSchemaFromZod(
1132
1051
  z.string(),
1133
1052
  z.object({
@@ -1222,54 +1141,86 @@ function useOwnedEntities(allowedKinds) {
1222
1141
  return useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]);
1223
1142
  }
1224
1143
 
1225
- const FIELD_EXTENSION_WRAPPER_KEY = "scaffolder.extensions.wrapper.v1";
1226
- const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
1227
- function createScaffolderFieldExtension(options) {
1228
- return {
1229
- expose() {
1230
- const FieldExtensionDataHolder = () => null;
1231
- attachComponentData(
1232
- FieldExtensionDataHolder,
1233
- FIELD_EXTENSION_KEY,
1234
- options
1235
- );
1236
- return FieldExtensionDataHolder;
1237
- }
1238
- };
1239
- }
1240
- function createNextScaffolderFieldExtension(options) {
1241
- return {
1242
- expose() {
1243
- const FieldExtensionDataHolder = () => null;
1244
- attachComponentData(
1245
- FieldExtensionDataHolder,
1246
- FIELD_EXTENSION_KEY,
1247
- options
1248
- );
1249
- return FieldExtensionDataHolder;
1250
- }
1251
- };
1252
- }
1253
- const ScaffolderFieldExtensions = () => null;
1254
- attachComponentData(
1255
- ScaffolderFieldExtensions,
1256
- FIELD_EXTENSION_WRAPPER_KEY,
1257
- true
1144
+ const EntityTagsPickerFieldSchema = makeFieldSchemaFromZod(
1145
+ z.array(z.string()),
1146
+ z.object({
1147
+ kinds: z.array(z.string()).optional().describe("List of kinds of entities to derive tags from"),
1148
+ showCounts: z.boolean().optional().describe("Whether to show usage counts per tag"),
1149
+ helperText: z.string().optional().describe("Helper text to display")
1150
+ })
1258
1151
  );
1152
+ const EntityTagsPickerSchema = EntityTagsPickerFieldSchema.schema;
1259
1153
 
1260
- const LAYOUTS_KEY = "scaffolder.layout.v1";
1261
- const LAYOUTS_WRAPPER_KEY = "scaffolder.layouts.wrapper.v1";
1262
- function createScaffolderLayout(options) {
1263
- return {
1264
- expose() {
1265
- const LayoutDataHolder = () => null;
1266
- attachComponentData(LayoutDataHolder, LAYOUTS_KEY, options);
1267
- return LayoutDataHolder;
1154
+ const EntityTagsPicker = (props) => {
1155
+ var _a, _b, _c;
1156
+ const { formData, onChange, uiSchema } = props;
1157
+ const catalogApi = useApi(catalogApiRef);
1158
+ const [tagOptions, setTagOptions] = useState([]);
1159
+ const [inputValue, setInputValue] = useState("");
1160
+ const [inputError, setInputError] = useState(false);
1161
+ const tagValidator = makeValidator().isValidTag;
1162
+ const kinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.kinds;
1163
+ const showCounts = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.showCounts;
1164
+ const helperText = (_c = uiSchema["ui:options"]) == null ? void 0 : _c.helperText;
1165
+ const { loading, value: existingTags } = useAsync(async () => {
1166
+ const facet = "metadata.tags";
1167
+ const tagsRequest = { facets: [facet] };
1168
+ if (kinds) {
1169
+ tagsRequest.filter = { kind: kinds };
1170
+ }
1171
+ const { facets } = await catalogApi.getEntityFacets(tagsRequest);
1172
+ const tagFacets = Object.fromEntries(
1173
+ facets[facet].map(({ value, count }) => [value, count])
1174
+ );
1175
+ setTagOptions(
1176
+ Object.keys(tagFacets).sort(
1177
+ (a, b) => showCounts ? tagFacets[b] - tagFacets[a] : a.localeCompare(b)
1178
+ )
1179
+ );
1180
+ return tagFacets;
1181
+ });
1182
+ const setTags = (_, values) => {
1183
+ let hasError = false;
1184
+ let addDuplicate = false;
1185
+ const currentTags = formData || [];
1186
+ if ((values == null ? void 0 : values.length) && currentTags.length < values.length) {
1187
+ const newTag = values[values.length - 1] = values[values.length - 1].toLocaleLowerCase("en-US").trim();
1188
+ hasError = !tagValidator(newTag);
1189
+ addDuplicate = currentTags.indexOf(newTag) !== -1;
1190
+ }
1191
+ setInputError(hasError);
1192
+ setInputValue(!hasError ? "" : inputValue);
1193
+ if (!hasError && !addDuplicate) {
1194
+ onChange(values || []);
1268
1195
  }
1269
1196
  };
1270
- }
1271
- const ScaffolderLayouts = () => null;
1272
- attachComponentData(ScaffolderLayouts, LAYOUTS_WRAPPER_KEY, true);
1197
+ useEffectOnce(() => onChange(formData || []));
1198
+ return /* @__PURE__ */ React.createElement(FormControl$1, { margin: "normal" }, /* @__PURE__ */ React.createElement(
1199
+ Autocomplete$1,
1200
+ {
1201
+ multiple: true,
1202
+ freeSolo: true,
1203
+ filterSelectedOptions: true,
1204
+ onChange: setTags,
1205
+ value: formData || [],
1206
+ inputValue,
1207
+ loading,
1208
+ options: tagOptions,
1209
+ ChipProps: { size: "small" },
1210
+ renderOption: (option) => showCounts ? `${option} (${existingTags == null ? void 0 : existingTags[option]})` : option,
1211
+ renderInput: (params) => /* @__PURE__ */ React.createElement(
1212
+ TextField,
1213
+ {
1214
+ ...params,
1215
+ label: "Tags",
1216
+ onChange: (e) => setInputValue(e.target.value),
1217
+ error: inputError,
1218
+ helperText: helperText != null ? helperText : "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters"
1219
+ }
1220
+ )
1221
+ }
1222
+ ));
1223
+ };
1273
1224
 
1274
1225
  const registerComponentRouteRef = createExternalRouteRef({
1275
1226
  id: "register-component",
@@ -1280,58 +1231,145 @@ const viewTechDocRouteRef = createExternalRouteRef({
1280
1231
  optional: true,
1281
1232
  params: ["namespace", "kind", "name"]
1282
1233
  });
1283
- const rootRouteRef = createRouteRef({
1234
+ const rootRouteRef$1 = createRouteRef({
1284
1235
  id: "scaffolder"
1285
1236
  });
1286
1237
  const legacySelectedTemplateRouteRef = createSubRouteRef({
1287
1238
  id: "scaffolder/legacy/selected-template",
1288
- parent: rootRouteRef,
1239
+ parent: rootRouteRef$1,
1289
1240
  path: "/templates/:templateName"
1290
1241
  });
1291
- const nextRouteRef = createRouteRef({
1292
- id: "scaffolder/next"
1293
- });
1294
1242
  const selectedTemplateRouteRef = createSubRouteRef({
1295
1243
  id: "scaffolder/selected-template",
1296
- parent: rootRouteRef,
1297
- path: "/templates/:namespace/:templateName"
1298
- });
1299
- const nextSelectedTemplateRouteRef = createSubRouteRef({
1300
- id: "scaffolder/next/selected-template",
1301
- parent: nextRouteRef,
1244
+ parent: rootRouteRef$1,
1302
1245
  path: "/templates/:namespace/:templateName"
1303
1246
  });
1304
1247
  const scaffolderTaskRouteRef = createSubRouteRef({
1305
1248
  id: "scaffolder/task",
1306
- parent: rootRouteRef,
1307
- path: "/tasks/:taskId"
1308
- });
1309
- createSubRouteRef({
1310
- id: "scaffolder/next/task",
1311
- parent: nextRouteRef,
1249
+ parent: rootRouteRef$1,
1312
1250
  path: "/tasks/:taskId"
1313
1251
  });
1314
1252
  const scaffolderListTaskRouteRef = createSubRouteRef({
1315
1253
  id: "scaffolder/list-tasks",
1316
- parent: rootRouteRef,
1254
+ parent: rootRouteRef$1,
1317
1255
  path: "/tasks"
1318
1256
  });
1319
1257
  const actionsRouteRef = createSubRouteRef({
1320
1258
  id: "scaffolder/actions",
1321
- parent: rootRouteRef,
1259
+ parent: rootRouteRef$1,
1322
1260
  path: "/actions"
1323
1261
  });
1324
1262
  const editRouteRef = createSubRouteRef({
1325
1263
  id: "scaffolder/edit",
1326
- parent: rootRouteRef,
1264
+ parent: rootRouteRef$1,
1327
1265
  path: "/edit"
1328
1266
  });
1329
1267
 
1268
+ const nextRouteRef = createRouteRef({
1269
+ id: "scaffolder/next"
1270
+ });
1271
+ const nextSelectedTemplateRouteRef = createSubRouteRef({
1272
+ id: "scaffolder/next/selected-template",
1273
+ parent: nextRouteRef,
1274
+ path: "/templates/:namespace/:templateName"
1275
+ });
1276
+ const nextScaffolderTaskRouteRef = createSubRouteRef({
1277
+ id: "scaffolder/next/task",
1278
+ parent: nextRouteRef,
1279
+ path: "/tasks/:taskId"
1280
+ });
1281
+
1282
+ const useStyles$2 = makeStyles(() => ({
1283
+ markdown: {
1284
+ /** to make the styles for React Markdown not leak into the description */
1285
+ "& :first-child": {
1286
+ marginTop: 0
1287
+ },
1288
+ "& :last-child": {
1289
+ marginBottom: 0
1290
+ }
1291
+ }
1292
+ }));
1293
+ const useTemplateParameterSchema = (templateRef) => {
1294
+ const scaffolderApi = useApi(scaffolderApiRef$1);
1295
+ const { value, loading, error } = useAsync(
1296
+ () => scaffolderApi.getTemplateParameterSchema(templateRef),
1297
+ [scaffolderApi, templateRef]
1298
+ );
1299
+ return { manifest: value, loading, error };
1300
+ };
1301
+ const TemplateWizardPage = (props) => {
1302
+ var _a;
1303
+ const styles = useStyles$2();
1304
+ const rootRef = useRouteRef(nextRouteRef);
1305
+ const taskRoute = useRouteRef(scaffolderTaskRouteRef);
1306
+ const { secrets } = useTemplateSecrets$1();
1307
+ const scaffolderApi = useApi(scaffolderApiRef$1);
1308
+ const navigate = useNavigate();
1309
+ const { templateName, namespace } = useRouteRefParams(
1310
+ selectedTemplateRouteRef
1311
+ );
1312
+ const templateRef = stringifyEntityRef({
1313
+ kind: "Template",
1314
+ namespace,
1315
+ name: templateName
1316
+ });
1317
+ const errorApi = useApi(errorApiRef);
1318
+ const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
1319
+ const onComplete = async (values) => {
1320
+ const { taskId } = await scaffolderApi.scaffold({
1321
+ templateRef,
1322
+ values,
1323
+ secrets
1324
+ });
1325
+ navigate(taskRoute({ taskId }));
1326
+ };
1327
+ useEffect(() => {
1328
+ if (error) {
1329
+ errorApi.post(new Error(`Failed to load template, ${error}`));
1330
+ }
1331
+ }, [error, errorApi]);
1332
+ if (error) {
1333
+ return /* @__PURE__ */ React.createElement(Navigate, { to: rootRef() });
1334
+ }
1335
+ return /* @__PURE__ */ React.createElement(AnalyticsContext, { attributes: { entityRef: templateRef } }, /* @__PURE__ */ React.createElement(Page, { themeId: "website" }, /* @__PURE__ */ React.createElement(
1336
+ Header,
1337
+ {
1338
+ pageTitleOverride: "Create a new component",
1339
+ title: "Create a new component",
1340
+ subtitle: "Create new software components using standard templates in your organization"
1341
+ }
1342
+ ), /* @__PURE__ */ React.createElement(Content, null, loading && /* @__PURE__ */ React.createElement(Progress, null), manifest && /* @__PURE__ */ React.createElement(
1343
+ InfoCard,
1344
+ {
1345
+ title: manifest.title,
1346
+ subheader: /* @__PURE__ */ React.createElement(
1347
+ MarkdownContent,
1348
+ {
1349
+ className: styles.markdown,
1350
+ content: (_a = manifest.description) != null ? _a : "No description"
1351
+ }
1352
+ ),
1353
+ noPadding: true,
1354
+ titleTypographyProps: { component: "h2" }
1355
+ },
1356
+ /* @__PURE__ */ React.createElement(
1357
+ Stepper,
1358
+ {
1359
+ manifest,
1360
+ extensions: props.customFieldExtensions,
1361
+ onComplete,
1362
+ FormProps: props.FormProps
1363
+ }
1364
+ )
1365
+ ))));
1366
+ };
1367
+
1330
1368
  const scaffolderPlugin = createPlugin({
1331
1369
  id: "scaffolder",
1332
1370
  apis: [
1333
1371
  createApiFactory({
1334
- api: scaffolderApiRef,
1372
+ api: scaffolderApiRef$1,
1335
1373
  deps: {
1336
1374
  discoveryApi: discoveryApiRef,
1337
1375
  scmIntegrationsApi: scmIntegrationsApiRef,
@@ -1347,7 +1385,9 @@ const scaffolderPlugin = createPlugin({
1347
1385
  })
1348
1386
  ],
1349
1387
  routes: {
1350
- root: rootRouteRef
1388
+ root: rootRouteRef$1,
1389
+ selectedTemplate: selectedTemplateRouteRef,
1390
+ ongoingTask: scaffolderTaskRouteRef
1351
1391
  },
1352
1392
  externalRoutes: {
1353
1393
  registerComponent: registerComponentRouteRef,
@@ -1355,14 +1395,14 @@ const scaffolderPlugin = createPlugin({
1355
1395
  }
1356
1396
  });
1357
1397
  const EntityPickerFieldExtension = scaffolderPlugin.provide(
1358
- createScaffolderFieldExtension({
1398
+ createScaffolderFieldExtension$1({
1359
1399
  component: EntityPicker,
1360
1400
  name: "EntityPicker",
1361
1401
  schema: EntityPickerSchema
1362
1402
  })
1363
1403
  );
1364
1404
  const EntityNamePickerFieldExtension = scaffolderPlugin.provide(
1365
- createScaffolderFieldExtension({
1405
+ createScaffolderFieldExtension$1({
1366
1406
  component: EntityNamePicker,
1367
1407
  name: "EntityNamePicker",
1368
1408
  validation: entityNamePickerValidation,
@@ -1370,7 +1410,7 @@ const EntityNamePickerFieldExtension = scaffolderPlugin.provide(
1370
1410
  })
1371
1411
  );
1372
1412
  const RepoUrlPickerFieldExtension = scaffolderPlugin.provide(
1373
- createScaffolderFieldExtension({
1413
+ createScaffolderFieldExtension$1({
1374
1414
  component: RepoUrlPicker,
1375
1415
  name: "RepoUrlPicker",
1376
1416
  validation: repoPickerValidation,
@@ -1378,7 +1418,7 @@ const RepoUrlPickerFieldExtension = scaffolderPlugin.provide(
1378
1418
  })
1379
1419
  );
1380
1420
  const OwnerPickerFieldExtension = scaffolderPlugin.provide(
1381
- createScaffolderFieldExtension({
1421
+ createScaffolderFieldExtension$1({
1382
1422
  component: OwnerPicker,
1383
1423
  name: "OwnerPicker",
1384
1424
  schema: OwnerPickerSchema
@@ -1387,19 +1427,19 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(
1387
1427
  const ScaffolderPage = scaffolderPlugin.provide(
1388
1428
  createRoutableExtension({
1389
1429
  name: "ScaffolderPage",
1390
- component: () => import('./Router-416b874d.esm.js').then((m) => m.Router),
1391
- mountPoint: rootRouteRef
1430
+ component: () => import('./Router-c499b201.esm.js').then((m) => m.Router),
1431
+ mountPoint: rootRouteRef$1
1392
1432
  })
1393
1433
  );
1394
1434
  const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(
1395
- createScaffolderFieldExtension({
1435
+ createScaffolderFieldExtension$1({
1396
1436
  component: OwnedEntityPicker,
1397
1437
  name: "OwnedEntityPicker",
1398
1438
  schema: OwnedEntityPickerSchema
1399
1439
  })
1400
1440
  );
1401
1441
  const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(
1402
- createScaffolderFieldExtension({
1442
+ createScaffolderFieldExtension$1({
1403
1443
  component: EntityTagsPicker,
1404
1444
  name: "EntityTagsPicker",
1405
1445
  schema: EntityTagsPickerSchema
@@ -1408,7 +1448,7 @@ const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(
1408
1448
  const NextScaffolderPage = scaffolderPlugin.provide(
1409
1449
  createRoutableExtension({
1410
1450
  name: "NextScaffolderPage",
1411
- component: () => import('./index-78c07979.esm.js').then((m) => m.Router),
1451
+ component: () => import('./index-6aee5f45.esm.js').then((m) => m.Router),
1412
1452
  mountPoint: nextRouteRef
1413
1453
  })
1414
1454
  );
@@ -1515,7 +1555,7 @@ function reducer(draft, action) {
1515
1555
  }
1516
1556
  }
1517
1557
  const useTaskEventStream = (taskId) => {
1518
- const scaffolderApi = useApi(scaffolderApiRef);
1558
+ const scaffolderApi = useApi(scaffolderApiRef$1);
1519
1559
  const [state, dispatch] = useImmerReducer(reducer, {
1520
1560
  loading: true,
1521
1561
  completed: false,
@@ -1733,7 +1773,7 @@ const TaskStatusStepper = memo(
1733
1773
  const { steps, currentStepId, onUserStepChange } = props;
1734
1774
  const classes = useStyles(props);
1735
1775
  return /* @__PURE__ */ React.createElement("div", { className: classes.root }, /* @__PURE__ */ React.createElement(
1736
- Stepper,
1776
+ Stepper$1,
1737
1777
  {
1738
1778
  activeStep: steps.findIndex((s) => s.id === currentStepId),
1739
1779
  orientation: "vertical",
@@ -1765,7 +1805,7 @@ const hasLinks = ({ links = [] }) => links.length > 0;
1765
1805
  const TaskPage = ({ loadingText }) => {
1766
1806
  const classes = useStyles();
1767
1807
  const navigate = useNavigate();
1768
- const rootPath = useRouteRef(rootRouteRef);
1808
+ const rootPath = useRouteRef(rootRouteRef$1);
1769
1809
  const templateRoute = useRouteRef(selectedTemplateRouteRef);
1770
1810
  const [userSelectedStepId, setUserSelectedStepId] = useState(void 0);
1771
1811
  const [lastActiveStepId, setLastActiveStepId] = useState(
@@ -1861,5 +1901,11 @@ const TaskPage = ({ loadingText }) => {
1861
1901
  ))), /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: 9 }, !currentStepId && /* @__PURE__ */ React.createElement(Progress, null), /* @__PURE__ */ React.createElement("div", { style: { height: "80vh" } }, /* @__PURE__ */ React.createElement(TaskErrors, { error: taskStream.error }), /* @__PURE__ */ React.createElement(LogViewer, { text: logAsString })))))));
1862
1902
  };
1863
1903
 
1864
- export { OwnerPickerFieldSchema as $, OwnedEntityPickerSchema as A, nextSelectedTemplateRouteRef as B, nextRouteRef as C, ScaffolderClient as D, EntityPicker as E, FIELD_EXTENSION_WRAPPER_KEY as F, createScaffolderFieldExtension as G, ScaffolderFieldExtensions as H, createScaffolderLayout as I, ScaffolderLayouts as J, EntityPickerFieldExtension as K, LAYOUTS_WRAPPER_KEY as L, EntityNamePickerFieldExtension as M, EntityTagsPickerFieldExtension as N, OwnerPicker as O, OwnerPickerFieldExtension as P, OwnedEntityPickerFieldExtension as Q, RepoUrlPicker as R, SecretsContext as S, TemplateTypePicker as T, RepoUrlPickerFieldExtension as U, ScaffolderPage as V, scaffolderPlugin as W, NextScaffolderPage as X, createNextScaffolderFieldExtension as Y, makeFieldSchemaFromZod as Z, EntityPickerFieldSchema as _, actionsRouteRef as a, RepoUrlPickerFieldSchema as a0, OwnedEntityPickerFieldSchema as a1, EntityTagsPickerFieldSchema as a2, useTemplateSecrets as a3, scaffolderListTaskRouteRef as b, scaffolderApiRef as c, scaffolderTaskRouteRef as d, editRouteRef as e, rootRouteRef as f, TaskStatusStepper as g, TaskPageLinks as h, FIELD_EXTENSION_KEY as i, LAYOUTS_KEY as j, SecretsContextProvider as k, legacySelectedTemplateRouteRef as l, TaskPage as m, EntityPickerSchema as n, EntityNamePicker as o, entityNamePickerValidation as p, EntityNamePickerSchema as q, registerComponentRouteRef as r, selectedTemplateRouteRef as s, EntityTagsPicker as t, EntityTagsPickerSchema as u, viewTechDocRouteRef as v, repoPickerValidation as w, RepoUrlPickerSchema as x, OwnerPickerSchema as y, OwnedEntityPicker as z };
1865
- //# sourceMappingURL=index-7e25101d.esm.js.map
1904
+ const rootRouteRef = rootRouteRef$1;
1905
+ const createScaffolderFieldExtension = createScaffolderFieldExtension$1;
1906
+ const ScaffolderFieldExtensions = ScaffolderFieldExtensions$1;
1907
+ const useTemplateSecrets = useTemplateSecrets$1;
1908
+ const scaffolderApiRef = scaffolderApiRef$1;
1909
+
1910
+ export { ScaffolderFieldExtensions as $, createScaffolderLayout as A, ScaffolderLayouts as B, EntityPickerFieldExtension as C, EntityNamePickerFieldExtension as D, EntityPicker as E, EntityTagsPickerFieldExtension as F, OwnerPickerFieldExtension as G, OwnedEntityPickerFieldExtension as H, RepoUrlPickerFieldExtension as I, ScaffolderPage as J, scaffolderPlugin as K, LAYOUTS_WRAPPER_KEY as L, nextRouteRef as M, NextScaffolderPage as N, OwnerPicker as O, nextScaffolderTaskRouteRef as P, makeFieldSchemaFromZod as Q, RepoUrlPicker as R, ScaffolderClient as S, TemplateTypePicker as T, EntityPickerFieldSchema as U, OwnerPickerFieldSchema as V, RepoUrlPickerFieldSchema as W, OwnedEntityPickerFieldSchema as X, EntityTagsPickerFieldSchema as Y, rootRouteRef as Z, createScaffolderFieldExtension as _, actionsRouteRef as a, useTemplateSecrets as a0, scaffolderApiRef as a1, scaffolderListTaskRouteRef as b, scaffolderTaskRouteRef as c, rootRouteRef$1 as d, editRouteRef as e, TaskStatusStepper as f, TaskPageLinks as g, LAYOUTS_KEY as h, TaskPage as i, EntityPickerSchema as j, EntityNamePicker as k, legacySelectedTemplateRouteRef as l, entityNamePickerValidation as m, EntityNamePickerSchema as n, EntityTagsPicker as o, EntityTagsPickerSchema as p, repoPickerValidation as q, registerComponentRouteRef as r, selectedTemplateRouteRef as s, RepoUrlPickerSchema as t, OwnerPickerSchema as u, viewTechDocRouteRef as v, OwnedEntityPicker as w, OwnedEntityPickerSchema as x, nextSelectedTemplateRouteRef as y, TemplateWizardPage as z };
1911
+ //# sourceMappingURL=index-618869a2.esm.js.map