@backstage/plugin-scaffolder-react 0.0.0-nightly-20230111022819

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.
@@ -0,0 +1,571 @@
1
+ import { attachComponentData, createApiRef, useElementFilter, useApi, featureFlagsApiRef, useAnalytics, useApiHolder, useApp } from '@backstage/core-plugin-api';
2
+ import { createVersionedContext, createVersionedValueMap, getOrCreateGlobalSingleton } from '@backstage/version-bridge';
3
+ import React, { useState, useContext, useCallback, useMemo } from 'react';
4
+ import { makeStyles, Stepper as Stepper$1, Step, StepLabel, Button, useTheme, Card, CardContent, Grid, Box, Divider, Chip, CardActions, Typography } from '@material-ui/core';
5
+ import { withTheme } from '@rjsf/core-v5';
6
+ import { Draft07 } from 'json-schema-library';
7
+ import { StructuredMetadataTable, ItemCardHeader, Link, MarkdownContent, UserIcon, Content, ItemCardGrid, ContentHeader } from '@backstage/core-components';
8
+ import validator from '@rjsf/validator-ajv6';
9
+ import qs from 'qs';
10
+ import { RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
11
+ import { FavoriteEntity, getEntityRelations, EntityRefLinks } from '@backstage/plugin-catalog-react';
12
+ import LanguageIcon from '@material-ui/icons/Language';
13
+
14
+ const FIELD_EXTENSION_WRAPPER_KEY = "scaffolder.extensions.wrapper.v1";
15
+ const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
16
+
17
+ function createScaffolderFieldExtension(options) {
18
+ return {
19
+ expose() {
20
+ const FieldExtensionDataHolder = () => null;
21
+ attachComponentData(
22
+ FieldExtensionDataHolder,
23
+ FIELD_EXTENSION_KEY,
24
+ options
25
+ );
26
+ return FieldExtensionDataHolder;
27
+ }
28
+ };
29
+ }
30
+ const ScaffolderFieldExtensions = () => null;
31
+ attachComponentData(
32
+ ScaffolderFieldExtensions,
33
+ FIELD_EXTENSION_WRAPPER_KEY,
34
+ true
35
+ );
36
+
37
+ const SecretsContext = createVersionedContext("secrets-context");
38
+ const SecretsContextProvider = ({ children }) => {
39
+ const [secrets, setSecrets] = useState({});
40
+ return /* @__PURE__ */ React.createElement(
41
+ SecretsContext.Provider,
42
+ {
43
+ value: createVersionedValueMap({ 1: { secrets, setSecrets } })
44
+ },
45
+ children
46
+ );
47
+ };
48
+ const useTemplateSecrets = () => {
49
+ var _a;
50
+ const value = (_a = useContext(SecretsContext)) == null ? void 0 : _a.atVersion(1);
51
+ if (!value) {
52
+ throw new Error(
53
+ "useTemplateSecrets must be used within a SecretsContextProvider"
54
+ );
55
+ }
56
+ const { setSecrets: updateSecrets, secrets = {} } = value;
57
+ const setSecrets = useCallback(
58
+ (input) => {
59
+ updateSecrets((currentSecrets) => ({ ...currentSecrets, ...input }));
60
+ },
61
+ [updateSecrets]
62
+ );
63
+ return { setSecrets, secrets };
64
+ };
65
+
66
+ const scaffolderApiRef = getOrCreateGlobalSingleton(
67
+ "scaffolder:scaffolder-api-ref",
68
+ () => createApiRef({
69
+ id: "plugin.scaffolder.service"
70
+ })
71
+ );
72
+
73
+ const useCustomFieldExtensions = (outlet) => {
74
+ return useElementFilter(
75
+ outlet,
76
+ (elements) => elements.selectByComponentData({
77
+ key: FIELD_EXTENSION_WRAPPER_KEY
78
+ }).findComponentData({
79
+ key: FIELD_EXTENSION_KEY
80
+ })
81
+ );
82
+ };
83
+
84
+ function isObject(value) {
85
+ return typeof value === "object" && value !== null && !Array.isArray(value);
86
+ }
87
+ function extractUiSchema(schema, uiSchema) {
88
+ if (!isObject(schema)) {
89
+ return;
90
+ }
91
+ const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;
92
+ for (const propName in schema) {
93
+ if (!schema.hasOwnProperty(propName)) {
94
+ continue;
95
+ }
96
+ if (propName.startsWith("ui:")) {
97
+ uiSchema[propName] = schema[propName];
98
+ delete schema[propName];
99
+ }
100
+ }
101
+ if (isObject(properties)) {
102
+ for (const propName in properties) {
103
+ if (!properties.hasOwnProperty(propName)) {
104
+ continue;
105
+ }
106
+ const schemaNode = properties[propName];
107
+ if (!isObject(schemaNode)) {
108
+ continue;
109
+ }
110
+ const innerUiSchema = {};
111
+ uiSchema[propName] = innerUiSchema;
112
+ extractUiSchema(schemaNode, innerUiSchema);
113
+ }
114
+ }
115
+ if (isObject(items)) {
116
+ const innerUiSchema = {};
117
+ uiSchema.items = innerUiSchema;
118
+ extractUiSchema(items, innerUiSchema);
119
+ }
120
+ if (Array.isArray(anyOf)) {
121
+ for (const schemaNode of anyOf) {
122
+ if (!isObject(schemaNode)) {
123
+ continue;
124
+ }
125
+ extractUiSchema(schemaNode, uiSchema);
126
+ }
127
+ }
128
+ if (Array.isArray(oneOf)) {
129
+ for (const schemaNode of oneOf) {
130
+ if (!isObject(schemaNode)) {
131
+ continue;
132
+ }
133
+ extractUiSchema(schemaNode, uiSchema);
134
+ }
135
+ }
136
+ if (Array.isArray(allOf)) {
137
+ for (const schemaNode of allOf) {
138
+ if (!isObject(schemaNode)) {
139
+ continue;
140
+ }
141
+ extractUiSchema(schemaNode, uiSchema);
142
+ }
143
+ }
144
+ if (isObject(dependencies)) {
145
+ for (const depName of Object.keys(dependencies)) {
146
+ const schemaNode = dependencies[depName];
147
+ if (!isObject(schemaNode)) {
148
+ continue;
149
+ }
150
+ extractUiSchema(schemaNode, uiSchema);
151
+ }
152
+ }
153
+ }
154
+ const extractSchemaFromStep = (inputStep) => {
155
+ const uiSchema = {};
156
+ const returnSchema = JSON.parse(JSON.stringify(inputStep));
157
+ extractUiSchema(returnSchema, uiSchema);
158
+ return { uiSchema, schema: returnSchema };
159
+ };
160
+ const createFieldValidation = () => {
161
+ const fieldValidation = {
162
+ __errors: [],
163
+ addError: (message) => {
164
+ var _a;
165
+ (_a = fieldValidation.__errors) == null ? void 0 : _a.push(message);
166
+ }
167
+ };
168
+ return fieldValidation;
169
+ };
170
+
171
+ const createAsyncValidators = (rootSchema, validators, context) => {
172
+ async function validate(formData, pathPrefix = "#") {
173
+ const parsedSchema = new Draft07(rootSchema);
174
+ const formValidation = {};
175
+ for (const [key, value] of Object.entries(formData)) {
176
+ const definitionInSchema = parsedSchema.getSchema(
177
+ `${pathPrefix}/${key}`,
178
+ formData
179
+ );
180
+ if (definitionInSchema && "ui:field" in definitionInSchema) {
181
+ const validator = validators[definitionInSchema["ui:field"]];
182
+ if (validator) {
183
+ const fieldValidation = createFieldValidation();
184
+ try {
185
+ await validator(value, fieldValidation, { ...context, formData });
186
+ } catch (ex) {
187
+ fieldValidation.addError(ex.message);
188
+ }
189
+ formValidation[key] = fieldValidation;
190
+ }
191
+ }
192
+ }
193
+ return formValidation;
194
+ }
195
+ return async (formData) => {
196
+ return await validate(formData);
197
+ };
198
+ };
199
+
200
+ const useTemplateSchema = (manifest) => {
201
+ const featureFlags = useApi(featureFlagsApiRef);
202
+ const steps = manifest.steps.map(({ title, description, schema }) => ({
203
+ title,
204
+ description,
205
+ mergedSchema: schema,
206
+ ...extractSchemaFromStep(schema)
207
+ }));
208
+ const returningSteps = steps.filter((step) => {
209
+ var _a;
210
+ const stepFeatureFlag = (_a = step.uiSchema["ui:backstage"]) == null ? void 0 : _a.featureFlag;
211
+ return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
212
+ }).map((step) => ({
213
+ ...step,
214
+ schema: {
215
+ ...step.schema,
216
+ // Title is rendered at the top of the page, so let's ignore this from jsonschemaform
217
+ title: void 0,
218
+ properties: Object.fromEntries(
219
+ Object.entries(step.schema.properties).filter(
220
+ ([key]) => {
221
+ var _a, _b;
222
+ const stepFeatureFlag = (_b = (_a = step.uiSchema[key]) == null ? void 0 : _a["ui:backstage"]) == null ? void 0 : _b.featureFlag;
223
+ return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
224
+ }
225
+ )
226
+ )
227
+ }
228
+ }));
229
+ return {
230
+ steps: returningSteps
231
+ };
232
+ };
233
+
234
+ const ReviewState = (props) => {
235
+ const reviewData = Object.fromEntries(
236
+ Object.entries(props.formState).map(([key, value]) => {
237
+ var _a;
238
+ for (const step of props.schemas) {
239
+ const parsedSchema = new Draft07(step.mergedSchema);
240
+ const definitionInSchema = parsedSchema.getSchema(
241
+ `#/${key}`,
242
+ props.formState
243
+ );
244
+ if (definitionInSchema) {
245
+ const backstageReviewOptions = (_a = definitionInSchema["ui:backstage"]) == null ? void 0 : _a.review;
246
+ if (backstageReviewOptions) {
247
+ if (backstageReviewOptions.mask) {
248
+ return [key, backstageReviewOptions.mask];
249
+ }
250
+ if (backstageReviewOptions.show === false) {
251
+ return [];
252
+ }
253
+ }
254
+ if (definitionInSchema["ui:widget"] === "password") {
255
+ return [key, "******"];
256
+ }
257
+ }
258
+ }
259
+ return [key, value];
260
+ })
261
+ );
262
+ return /* @__PURE__ */ React.createElement(StructuredMetadataTable, { metadata: reviewData });
263
+ };
264
+
265
+ const useFormDataFromQuery = (initialState) => {
266
+ return useState(() => {
267
+ if (initialState) {
268
+ return initialState;
269
+ }
270
+ const query = qs.parse(window.location.search, {
271
+ ignoreQueryPrefix: true
272
+ });
273
+ try {
274
+ return JSON.parse(query.formData);
275
+ } catch (e) {
276
+ return {};
277
+ }
278
+ });
279
+ };
280
+
281
+ const useStyles$3 = makeStyles((theme) => ({
282
+ backButton: {
283
+ marginRight: theme.spacing(1)
284
+ },
285
+ footer: {
286
+ display: "flex",
287
+ flexDirection: "row",
288
+ justifyContent: "right"
289
+ },
290
+ formWrapper: {
291
+ padding: theme.spacing(2)
292
+ }
293
+ }));
294
+ const Form = withTheme(require("@rjsf/material-ui-v5").Theme);
295
+ const Stepper = (props) => {
296
+ var _a;
297
+ const analytics = useAnalytics();
298
+ const { steps } = useTemplateSchema(props.manifest);
299
+ const apiHolder = useApiHolder();
300
+ const [activeStep, setActiveStep] = useState(0);
301
+ const [formState, setFormState] = useFormDataFromQuery(props.initialState);
302
+ const [errors, setErrors] = useState();
303
+ const styles = useStyles$3();
304
+ const extensions = useMemo(() => {
305
+ return Object.fromEntries(
306
+ props.extensions.map(({ name, component }) => [name, component])
307
+ );
308
+ }, [props.extensions]);
309
+ const validators = useMemo(() => {
310
+ return Object.fromEntries(
311
+ props.extensions.map(({ name, validation: validation2 }) => [name, validation2])
312
+ );
313
+ }, [props.extensions]);
314
+ const validation = useMemo(() => {
315
+ var _a2;
316
+ return createAsyncValidators((_a2 = steps[activeStep]) == null ? void 0 : _a2.mergedSchema, validators, {
317
+ apiHolder
318
+ });
319
+ }, [steps, activeStep, validators, apiHolder]);
320
+ const handleBack = () => {
321
+ setActiveStep((prevActiveStep) => prevActiveStep - 1);
322
+ };
323
+ const handleChange = useCallback(
324
+ (e) => setFormState((current) => ({ ...current, ...e.formData })),
325
+ [setFormState]
326
+ );
327
+ const handleNext = async ({
328
+ formData
329
+ }) => {
330
+ setErrors(void 0);
331
+ const returnedValidation = await validation(formData);
332
+ const hasErrors = Object.values(returnedValidation).some(
333
+ (i) => {
334
+ var _a2;
335
+ return (_a2 = i.__errors) == null ? void 0 : _a2.length;
336
+ }
337
+ );
338
+ if (hasErrors) {
339
+ setErrors(returnedValidation);
340
+ } else {
341
+ setErrors(void 0);
342
+ setActiveStep((prevActiveStep) => {
343
+ const stepNum = prevActiveStep + 1;
344
+ analytics.captureEvent("click", `Next Step (${stepNum})`);
345
+ return stepNum;
346
+ });
347
+ }
348
+ setFormState((current) => ({ ...current, ...formData }));
349
+ };
350
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Stepper$1, { activeStep, alternativeLabel: true, variant: "elevation" }, steps.map((step, index) => /* @__PURE__ */ React.createElement(Step, { key: index }, /* @__PURE__ */ React.createElement(StepLabel, null, step.title))), /* @__PURE__ */ React.createElement(Step, null, /* @__PURE__ */ React.createElement(StepLabel, null, "Review"))), /* @__PURE__ */ React.createElement("div", { className: styles.formWrapper }, activeStep < steps.length ? /* @__PURE__ */ React.createElement(
351
+ Form,
352
+ {
353
+ validator,
354
+ extraErrors: errors,
355
+ formData: formState,
356
+ formContext: { formData: formState },
357
+ schema: steps[activeStep].schema,
358
+ uiSchema: steps[activeStep].uiSchema,
359
+ onSubmit: handleNext,
360
+ fields: extensions,
361
+ showErrorList: false,
362
+ onChange: handleChange,
363
+ ...(_a = props.FormProps) != null ? _a : {}
364
+ },
365
+ /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
366
+ Button,
367
+ {
368
+ onClick: handleBack,
369
+ className: styles.backButton,
370
+ disabled: activeStep < 1
371
+ },
372
+ "Back"
373
+ ), /* @__PURE__ */ React.createElement(Button, { variant: "contained", color: "primary", type: "submit" }, activeStep === steps.length - 1 ? "Review" : "Next"))
374
+ ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ReviewState, { formState, schemas: steps }), /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
375
+ Button,
376
+ {
377
+ onClick: handleBack,
378
+ className: styles.backButton,
379
+ disabled: activeStep < 1
380
+ },
381
+ "Back"
382
+ ), /* @__PURE__ */ React.createElement(
383
+ Button,
384
+ {
385
+ variant: "contained",
386
+ onClick: () => {
387
+ var _a2;
388
+ props.onComplete(formState);
389
+ const name = typeof formState.name === "string" ? formState.name : void 0;
390
+ analytics.captureEvent(
391
+ "create",
392
+ (_a2 = name != null ? name : props.templateName) != null ? _a2 : "unknown"
393
+ );
394
+ }
395
+ },
396
+ "Create"
397
+ )))));
398
+ };
399
+
400
+ const useStyles$2 = makeStyles(
401
+ () => ({
402
+ header: {
403
+ backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage
404
+ },
405
+ subtitleWrapper: {
406
+ display: "flex",
407
+ justifyContent: "space-between"
408
+ }
409
+ })
410
+ );
411
+ const CardHeader = (props) => {
412
+ const {
413
+ template: {
414
+ metadata: { title, name },
415
+ spec: { type }
416
+ }
417
+ } = props;
418
+ const { getPageTheme } = useTheme();
419
+ const themeForType = getPageTheme({ themeId: type });
420
+ const styles = useStyles$2({
421
+ cardBackgroundImage: themeForType.backgroundImage
422
+ });
423
+ const SubtitleComponent = /* @__PURE__ */ React.createElement("div", { className: styles.subtitleWrapper }, /* @__PURE__ */ React.createElement("div", null, type), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(FavoriteEntity, { entity: props.template, style: { padding: 0 } })));
424
+ return /* @__PURE__ */ React.createElement(
425
+ ItemCardHeader,
426
+ {
427
+ title: title != null ? title : name,
428
+ subtitle: SubtitleComponent,
429
+ classes: { root: styles.header }
430
+ }
431
+ );
432
+ };
433
+
434
+ const useStyles$1 = makeStyles(() => ({
435
+ linkText: {
436
+ display: "inline-flex",
437
+ alignItems: "center"
438
+ }
439
+ }));
440
+ const CardLink = ({ icon: Icon, text, url }) => {
441
+ const styles = useStyles$1();
442
+ return /* @__PURE__ */ React.createElement("div", { className: styles.linkText }, /* @__PURE__ */ React.createElement(Icon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(Link, { style: { marginLeft: "8px" }, to: url }, text || url));
443
+ };
444
+
445
+ const useStyles = makeStyles((theme) => ({
446
+ box: {
447
+ overflow: "hidden",
448
+ textOverflow: "ellipsis",
449
+ display: "-webkit-box",
450
+ "-webkit-line-clamp": 10,
451
+ "-webkit-box-orient": "vertical"
452
+ },
453
+ markdown: {
454
+ /** to make the styles for React Markdown not leak into the description */
455
+ "& :first-child": {
456
+ margin: 0
457
+ }
458
+ },
459
+ label: {
460
+ color: theme.palette.text.secondary,
461
+ textTransform: "uppercase",
462
+ fontWeight: "bold",
463
+ letterSpacing: 0.5,
464
+ lineHeight: 1,
465
+ fontSize: "0.75rem"
466
+ },
467
+ footer: {
468
+ display: "flex",
469
+ justifyContent: "space-between",
470
+ flex: 1,
471
+ alignItems: "center"
472
+ },
473
+ ownedBy: {
474
+ display: "flex",
475
+ alignItems: "center",
476
+ flex: 1,
477
+ color: theme.palette.link
478
+ }
479
+ }));
480
+ const TemplateCard = (props) => {
481
+ var _a, _b, _c, _d, _e, _f, _g;
482
+ const { template } = props;
483
+ const styles = useStyles();
484
+ const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
485
+ const app = useApp();
486
+ const iconResolver = (key) => {
487
+ var _a2;
488
+ return key ? (_a2 = app.getSystemIcon(key)) != null ? _a2 : LanguageIcon : LanguageIcon;
489
+ };
490
+ return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { template }), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Box, { className: styles.box }, /* @__PURE__ */ React.createElement(
491
+ MarkdownContent,
492
+ {
493
+ className: styles.markdown,
494
+ content: (_a = template.metadata.description) != null ? _a : "No description"
495
+ }
496
+ ))), ((_c = (_b = template.metadata.tags) == null ? void 0 : _b.length) != null ? _c : 0) > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, (_d = template.metadata.tags) == null ? void 0 : _d.map((tag) => /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
497
+ Chip,
498
+ {
499
+ style: { margin: 0 },
500
+ size: "small",
501
+ label: tag,
502
+ key: tag
503
+ }
504
+ )))))), (props.additionalLinks || ((_e = template.metadata.links) == null ? void 0 : _e.length)) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, (_f = props.additionalLinks) == null ? void 0 : _f.map(({ icon, text, url }) => /* @__PURE__ */ React.createElement(Grid, { className: styles.linkText, item: true, xs: 6 }, /* @__PURE__ */ React.createElement(CardLink, { icon, text, url }))), (_g = template.metadata.links) == null ? void 0 : _g.map(({ url, icon, title }) => /* @__PURE__ */ React.createElement(Grid, { className: styles.linkText, item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
505
+ CardLink,
506
+ {
507
+ icon: iconResolver(icon),
508
+ text: title || url,
509
+ url
510
+ }
511
+ )))))))), /* @__PURE__ */ React.createElement(CardActions, { style: { padding: "16px" } }, /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement("div", { className: styles.ownedBy }, ownedByRelations.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(UserIcon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(
512
+ EntityRefLinks,
513
+ {
514
+ style: { marginLeft: "8px" },
515
+ entityRefs: ownedByRelations,
516
+ defaultKind: "Group"
517
+ }
518
+ ))), /* @__PURE__ */ React.createElement(
519
+ Button,
520
+ {
521
+ size: "small",
522
+ variant: "outlined",
523
+ color: "primary",
524
+ onClick: () => {
525
+ var _a2;
526
+ return (_a2 = props.onSelected) == null ? void 0 : _a2.call(props, template);
527
+ }
528
+ },
529
+ "Choose"
530
+ ))));
531
+ };
532
+
533
+ const TemplateGroup = (props) => {
534
+ const {
535
+ templates,
536
+ title,
537
+ components: { CardComponent } = {},
538
+ onSelected
539
+ } = props;
540
+ const titleComponent = typeof title === "string" ? /* @__PURE__ */ React.createElement(ContentHeader, { title }) : title;
541
+ if (templates.length === 0) {
542
+ return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://backstage.io/docs/features/software-templates/adding-templates" }, "adding templates"), "."));
543
+ }
544
+ const Card = CardComponent || TemplateCard;
545
+ return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(ItemCardGrid, null, templates.map(({ template, additionalLinks }) => /* @__PURE__ */ React.createElement(
546
+ Card,
547
+ {
548
+ key: stringifyEntityRef(template),
549
+ additionalLinks,
550
+ template,
551
+ onSelected
552
+ }
553
+ ))));
554
+ };
555
+
556
+ function createNextScaffolderFieldExtension(options) {
557
+ return {
558
+ expose() {
559
+ const FieldExtensionDataHolder = () => null;
560
+ attachComponentData(
561
+ FieldExtensionDataHolder,
562
+ FIELD_EXTENSION_KEY,
563
+ options
564
+ );
565
+ return FieldExtensionDataHolder;
566
+ }
567
+ };
568
+ }
569
+
570
+ export { ReviewState, ScaffolderFieldExtensions, SecretsContextProvider, Stepper, TemplateCard, TemplateGroup, createFieldValidation, createNextScaffolderFieldExtension, createScaffolderFieldExtension, extractSchemaFromStep, scaffolderApiRef, useCustomFieldExtensions, useFormDataFromQuery, useTemplateSchema, useTemplateSecrets };
571
+ //# sourceMappingURL=index.esm.js.map