@backstage/plugin-scaffolder-react 1.1.0 → 1.2.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/alpha/package.json +4 -3
- package/dist/alpha.d.ts +259 -0
- package/dist/alpha.esm.js +851 -0
- package/dist/alpha.esm.js.map +1 -0
- package/dist/index.d.ts +211 -272
- package/dist/index.esm.js +119 -647
- package/dist/index.esm.js.map +1 -1
- package/package.json +41 -23
- package/dist/index.alpha.d.ts +0 -591
- package/dist/index.beta.d.ts +0 -425
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
import { useApi, featureFlagsApiRef, createApiRef, useAnalytics, useApiHolder, useApp, errorApiRef, useRouteRef, attachComponentData } from '@backstage/core-plugin-api';
|
|
2
|
+
import { makeStyles, Stepper as Stepper$1, Step, StepLabel, Button, useTheme, Card, CardContent, Grid, Box, Divider, Chip, CardActions, Typography, Paper, CircularProgress, LinearProgress, StepButton } from '@material-ui/core';
|
|
3
|
+
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
|
4
|
+
import { Draft07 } from 'json-schema-library';
|
|
5
|
+
import { StructuredMetadataTable, MarkdownContent, ItemCardHeader, Link, UserIcon, Content, ItemCardGrid, ContentHeader, Progress, InfoCard, LogViewer } from '@backstage/core-components';
|
|
6
|
+
import validator from '@rjsf/validator-ajv8';
|
|
7
|
+
import qs from 'qs';
|
|
8
|
+
import useAsync from 'react-use/lib/useAsync';
|
|
9
|
+
import { getOrCreateGlobalSingleton, createVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
|
|
10
|
+
import { withTheme } from '@rjsf/core-v5';
|
|
11
|
+
import { RELATION_OWNED_BY, stringifyEntityRef, parseEntityRef } from '@backstage/catalog-model';
|
|
12
|
+
import { FavoriteEntity, getEntityRelations, EntityRefLinks, entityRouteRef } from '@backstage/plugin-catalog-react';
|
|
13
|
+
import LanguageIcon from '@material-ui/icons/Language';
|
|
14
|
+
import WebIcon from '@material-ui/icons/Web';
|
|
15
|
+
import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
|
|
16
|
+
import PanoramaFishEyeIcon from '@material-ui/icons/PanoramaFishEye';
|
|
17
|
+
import classNames from 'classnames';
|
|
18
|
+
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
|
|
19
|
+
import ErrorOutline from '@material-ui/icons/ErrorOutline';
|
|
20
|
+
import useInterval from 'react-use/lib/useInterval';
|
|
21
|
+
import { DateTime, Interval } from 'luxon';
|
|
22
|
+
import humanizeDuration from 'humanize-duration';
|
|
23
|
+
import { useMountEffect } from '@react-hookz/web';
|
|
24
|
+
import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
|
|
25
|
+
|
|
26
|
+
function isObject$1(value) {
|
|
27
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
function extractUiSchema(schema, uiSchema) {
|
|
30
|
+
if (!isObject$1(schema)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;
|
|
34
|
+
for (const propName in schema) {
|
|
35
|
+
if (!schema.hasOwnProperty(propName)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (propName.startsWith("ui:")) {
|
|
39
|
+
uiSchema[propName] = schema[propName];
|
|
40
|
+
delete schema[propName];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (isObject$1(properties)) {
|
|
44
|
+
for (const propName in properties) {
|
|
45
|
+
if (!properties.hasOwnProperty(propName)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const schemaNode = properties[propName];
|
|
49
|
+
if (!isObject$1(schemaNode)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const innerUiSchema = {};
|
|
53
|
+
uiSchema[propName] = innerUiSchema;
|
|
54
|
+
extractUiSchema(schemaNode, innerUiSchema);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (isObject$1(items)) {
|
|
58
|
+
const innerUiSchema = {};
|
|
59
|
+
uiSchema.items = innerUiSchema;
|
|
60
|
+
extractUiSchema(items, innerUiSchema);
|
|
61
|
+
}
|
|
62
|
+
if (Array.isArray(anyOf)) {
|
|
63
|
+
for (const schemaNode of anyOf) {
|
|
64
|
+
if (!isObject$1(schemaNode)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
extractUiSchema(schemaNode, uiSchema);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(oneOf)) {
|
|
71
|
+
for (const schemaNode of oneOf) {
|
|
72
|
+
if (!isObject$1(schemaNode)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
extractUiSchema(schemaNode, uiSchema);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(allOf)) {
|
|
79
|
+
for (const schemaNode of allOf) {
|
|
80
|
+
if (!isObject$1(schemaNode)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
extractUiSchema(schemaNode, uiSchema);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (isObject$1(dependencies)) {
|
|
87
|
+
for (const depName of Object.keys(dependencies)) {
|
|
88
|
+
const schemaNode = dependencies[depName];
|
|
89
|
+
if (!isObject$1(schemaNode)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
extractUiSchema(schemaNode, uiSchema);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const extractSchemaFromStep = (inputStep) => {
|
|
97
|
+
const uiSchema = {};
|
|
98
|
+
const returnSchema = JSON.parse(JSON.stringify(inputStep));
|
|
99
|
+
extractUiSchema(returnSchema, uiSchema);
|
|
100
|
+
return { uiSchema, schema: returnSchema };
|
|
101
|
+
};
|
|
102
|
+
const createFieldValidation = () => {
|
|
103
|
+
const fieldValidation = {
|
|
104
|
+
__errors: [],
|
|
105
|
+
addError: (message) => {
|
|
106
|
+
var _a;
|
|
107
|
+
(_a = fieldValidation.__errors) == null ? void 0 : _a.push(message);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
return fieldValidation;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function isFieldValidation(error) {
|
|
114
|
+
return !!error && "__errors" in error;
|
|
115
|
+
}
|
|
116
|
+
function hasErrors(errors) {
|
|
117
|
+
var _a;
|
|
118
|
+
if (!errors) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
for (const error of Object.values(errors)) {
|
|
122
|
+
if (isFieldValidation(error)) {
|
|
123
|
+
if (((_a = error.__errors) != null ? _a : []).length > 0) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (hasErrors(error)) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
function isObject(value) {
|
|
135
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const createAsyncValidators = (rootSchema, validators, context) => {
|
|
139
|
+
async function validate(formData, pathPrefix = "#", current = formData) {
|
|
140
|
+
const parsedSchema = new Draft07(rootSchema);
|
|
141
|
+
const formValidation = {};
|
|
142
|
+
const validateForm = async (validatorName, key, value, schema, uiSchema) => {
|
|
143
|
+
const validator = validators[validatorName];
|
|
144
|
+
if (validator) {
|
|
145
|
+
const fieldValidation = createFieldValidation();
|
|
146
|
+
try {
|
|
147
|
+
await validator(value, fieldValidation, {
|
|
148
|
+
...context,
|
|
149
|
+
formData,
|
|
150
|
+
schema,
|
|
151
|
+
uiSchema
|
|
152
|
+
});
|
|
153
|
+
} catch (ex) {
|
|
154
|
+
fieldValidation.addError(ex.message);
|
|
155
|
+
}
|
|
156
|
+
formValidation[key] = fieldValidation;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
for (const [key, value] of Object.entries(current)) {
|
|
160
|
+
const path = `${pathPrefix}/${key}`;
|
|
161
|
+
const definitionInSchema = parsedSchema.getSchema(path, formData);
|
|
162
|
+
const { schema, uiSchema } = extractSchemaFromStep(definitionInSchema);
|
|
163
|
+
if (definitionInSchema && "ui:field" in definitionInSchema) {
|
|
164
|
+
if ("ui:field" in definitionInSchema) {
|
|
165
|
+
await validateForm(
|
|
166
|
+
definitionInSchema["ui:field"],
|
|
167
|
+
key,
|
|
168
|
+
value,
|
|
169
|
+
schema,
|
|
170
|
+
uiSchema
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
} else if (definitionInSchema && definitionInSchema.items && "ui:field" in definitionInSchema.items) {
|
|
174
|
+
if ("ui:field" in definitionInSchema.items) {
|
|
175
|
+
const { schema: itemsSchema, uiSchema: itemsUiSchema } = extractSchemaFromStep(definitionInSchema.items);
|
|
176
|
+
await validateForm(
|
|
177
|
+
definitionInSchema.items["ui:field"],
|
|
178
|
+
key,
|
|
179
|
+
value,
|
|
180
|
+
itemsSchema,
|
|
181
|
+
itemsUiSchema
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
} else if (isObject(value)) {
|
|
185
|
+
formValidation[key] = await validate(formData, path, value);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return formValidation;
|
|
189
|
+
}
|
|
190
|
+
return async (formData) => {
|
|
191
|
+
return await validate(formData);
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const ReviewState = (props) => {
|
|
196
|
+
const reviewData = Object.fromEntries(
|
|
197
|
+
Object.entries(props.formState).map(([key, value]) => {
|
|
198
|
+
var _a;
|
|
199
|
+
for (const step of props.schemas) {
|
|
200
|
+
const parsedSchema = new Draft07(step.mergedSchema);
|
|
201
|
+
const definitionInSchema = parsedSchema.getSchema(
|
|
202
|
+
`#/${key}`,
|
|
203
|
+
props.formState
|
|
204
|
+
);
|
|
205
|
+
if (definitionInSchema) {
|
|
206
|
+
const backstageReviewOptions = (_a = definitionInSchema["ui:backstage"]) == null ? void 0 : _a.review;
|
|
207
|
+
if (backstageReviewOptions) {
|
|
208
|
+
if (backstageReviewOptions.mask) {
|
|
209
|
+
return [key, backstageReviewOptions.mask];
|
|
210
|
+
}
|
|
211
|
+
if (backstageReviewOptions.show === false) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (definitionInSchema["ui:widget"] === "password") {
|
|
216
|
+
return [key, "******"];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return [key, value];
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
return /* @__PURE__ */ React.createElement(StructuredMetadataTable, { metadata: reviewData });
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const useTemplateSchema = (manifest) => {
|
|
227
|
+
const featureFlags = useApi(featureFlagsApiRef);
|
|
228
|
+
const steps = manifest.steps.map(({ title, description, schema }) => ({
|
|
229
|
+
title,
|
|
230
|
+
description,
|
|
231
|
+
mergedSchema: schema,
|
|
232
|
+
...extractSchemaFromStep(schema)
|
|
233
|
+
}));
|
|
234
|
+
const returningSteps = steps.filter((step) => {
|
|
235
|
+
var _a;
|
|
236
|
+
const stepFeatureFlag = (_a = step.uiSchema["ui:backstage"]) == null ? void 0 : _a.featureFlag;
|
|
237
|
+
return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
|
|
238
|
+
}).map((step) => ({
|
|
239
|
+
...step,
|
|
240
|
+
schema: {
|
|
241
|
+
...step.schema,
|
|
242
|
+
// Title is rendered at the top of the page, so let's ignore this from jsonschemaform
|
|
243
|
+
title: void 0,
|
|
244
|
+
properties: Object.fromEntries(
|
|
245
|
+
Object.entries(step.schema.properties).filter(
|
|
246
|
+
([key]) => {
|
|
247
|
+
var _a, _b;
|
|
248
|
+
const stepFeatureFlag = (_b = (_a = step.uiSchema[key]) == null ? void 0 : _a["ui:backstage"]) == null ? void 0 : _b.featureFlag;
|
|
249
|
+
return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
}));
|
|
255
|
+
return {
|
|
256
|
+
steps: returningSteps
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const useFormDataFromQuery = (initialState) => {
|
|
261
|
+
return useState(() => {
|
|
262
|
+
if (initialState) {
|
|
263
|
+
return initialState;
|
|
264
|
+
}
|
|
265
|
+
const query = qs.parse(window.location.search, {
|
|
266
|
+
ignoreQueryPrefix: true
|
|
267
|
+
});
|
|
268
|
+
try {
|
|
269
|
+
return JSON.parse(query.formData);
|
|
270
|
+
} catch (e) {
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const scaffolderApiRef = getOrCreateGlobalSingleton(
|
|
277
|
+
"scaffolder:scaffolder-api-ref",
|
|
278
|
+
() => createApiRef({
|
|
279
|
+
id: "plugin.scaffolder.service"
|
|
280
|
+
})
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const useTemplateParameterSchema = (templateRef) => {
|
|
284
|
+
const scaffolderApi = useApi(scaffolderApiRef);
|
|
285
|
+
const { value, loading, error } = useAsync(
|
|
286
|
+
() => scaffolderApi.getTemplateParameterSchema(templateRef),
|
|
287
|
+
[scaffolderApi, templateRef]
|
|
288
|
+
);
|
|
289
|
+
return { manifest: value, loading, error };
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const useTransformSchemaToProps = (step, options = {}) => {
|
|
293
|
+
var _a;
|
|
294
|
+
const { layouts = [] } = options;
|
|
295
|
+
const objectFieldTemplate = step == null ? void 0 : step.uiSchema["ui:ObjectFieldTemplate"];
|
|
296
|
+
if (typeof objectFieldTemplate !== "string") {
|
|
297
|
+
return step;
|
|
298
|
+
}
|
|
299
|
+
const Layout = (_a = layouts.find(
|
|
300
|
+
(layout) => layout.name === objectFieldTemplate
|
|
301
|
+
)) == null ? void 0 : _a.component;
|
|
302
|
+
if (!Layout) {
|
|
303
|
+
return step;
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
...step,
|
|
307
|
+
uiSchema: {
|
|
308
|
+
...step.uiSchema,
|
|
309
|
+
["ui:ObjectFieldTemplate"]: Layout
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const DescriptionField = ({ description }) => description && /* @__PURE__ */ React.createElement(MarkdownContent, { content: description, linkTarget: "_blank" });
|
|
315
|
+
|
|
316
|
+
var FieldOverrides = /*#__PURE__*/Object.freeze({
|
|
317
|
+
__proto__: null,
|
|
318
|
+
DescriptionField: DescriptionField
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const Form = withTheme(require("@rjsf/material-ui-v5").Theme);
|
|
322
|
+
|
|
323
|
+
const useStyles$7 = makeStyles((theme) => ({
|
|
324
|
+
backButton: {
|
|
325
|
+
marginRight: theme.spacing(1)
|
|
326
|
+
},
|
|
327
|
+
footer: {
|
|
328
|
+
display: "flex",
|
|
329
|
+
flexDirection: "row",
|
|
330
|
+
justifyContent: "right"
|
|
331
|
+
},
|
|
332
|
+
formWrapper: {
|
|
333
|
+
padding: theme.spacing(2)
|
|
334
|
+
}
|
|
335
|
+
}));
|
|
336
|
+
const Stepper = (stepperProps) => {
|
|
337
|
+
var _a;
|
|
338
|
+
const { layouts = [], components = {}, ...props } = stepperProps;
|
|
339
|
+
const {
|
|
340
|
+
ReviewStateComponent = ReviewState,
|
|
341
|
+
createButtonText = "Create",
|
|
342
|
+
reviewButtonText = "Review"
|
|
343
|
+
} = components;
|
|
344
|
+
const analytics = useAnalytics();
|
|
345
|
+
const { steps } = useTemplateSchema(props.manifest);
|
|
346
|
+
const apiHolder = useApiHolder();
|
|
347
|
+
const [activeStep, setActiveStep] = useState(0);
|
|
348
|
+
const [formState, setFormState] = useFormDataFromQuery(props.initialState);
|
|
349
|
+
const [errors, setErrors] = useState();
|
|
350
|
+
const styles = useStyles$7();
|
|
351
|
+
const extensions = useMemo(() => {
|
|
352
|
+
return Object.fromEntries(
|
|
353
|
+
props.extensions.map(({ name, component }) => [name, component])
|
|
354
|
+
);
|
|
355
|
+
}, [props.extensions]);
|
|
356
|
+
const validators = useMemo(() => {
|
|
357
|
+
return Object.fromEntries(
|
|
358
|
+
props.extensions.map(({ name, validation: validation2 }) => [name, validation2])
|
|
359
|
+
);
|
|
360
|
+
}, [props.extensions]);
|
|
361
|
+
const validation = useMemo(() => {
|
|
362
|
+
var _a2;
|
|
363
|
+
return createAsyncValidators((_a2 = steps[activeStep]) == null ? void 0 : _a2.mergedSchema, validators, {
|
|
364
|
+
apiHolder
|
|
365
|
+
});
|
|
366
|
+
}, [steps, activeStep, validators, apiHolder]);
|
|
367
|
+
const handleBack = () => {
|
|
368
|
+
setActiveStep((prevActiveStep) => prevActiveStep - 1);
|
|
369
|
+
};
|
|
370
|
+
const handleChange = useCallback(
|
|
371
|
+
(e) => setFormState((current) => ({ ...current, ...e.formData })),
|
|
372
|
+
[setFormState]
|
|
373
|
+
);
|
|
374
|
+
const currentStep = useTransformSchemaToProps(steps[activeStep], { layouts });
|
|
375
|
+
const handleNext = async ({
|
|
376
|
+
formData = {}
|
|
377
|
+
}) => {
|
|
378
|
+
setErrors(void 0);
|
|
379
|
+
const returnedValidation = await validation(formData);
|
|
380
|
+
if (hasErrors(returnedValidation)) {
|
|
381
|
+
setErrors(returnedValidation);
|
|
382
|
+
} else {
|
|
383
|
+
setErrors(void 0);
|
|
384
|
+
setActiveStep((prevActiveStep) => {
|
|
385
|
+
const stepNum = prevActiveStep + 1;
|
|
386
|
+
analytics.captureEvent("click", `Next Step (${stepNum})`);
|
|
387
|
+
return stepNum;
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
setFormState((current) => ({ ...current, ...formData }));
|
|
391
|
+
};
|
|
392
|
+
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(
|
|
393
|
+
Form,
|
|
394
|
+
{
|
|
395
|
+
validator,
|
|
396
|
+
extraErrors: errors,
|
|
397
|
+
formData: formState,
|
|
398
|
+
formContext: { formData: formState },
|
|
399
|
+
schema: currentStep.schema,
|
|
400
|
+
uiSchema: currentStep.uiSchema,
|
|
401
|
+
onSubmit: handleNext,
|
|
402
|
+
fields: { ...FieldOverrides, ...extensions },
|
|
403
|
+
showErrorList: false,
|
|
404
|
+
onChange: handleChange,
|
|
405
|
+
...(_a = props.FormProps) != null ? _a : {}
|
|
406
|
+
},
|
|
407
|
+
/* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
|
|
408
|
+
Button,
|
|
409
|
+
{
|
|
410
|
+
onClick: handleBack,
|
|
411
|
+
className: styles.backButton,
|
|
412
|
+
disabled: activeStep < 1
|
|
413
|
+
},
|
|
414
|
+
"Back"
|
|
415
|
+
), /* @__PURE__ */ React.createElement(Button, { variant: "contained", color: "primary", type: "submit" }, activeStep === steps.length - 1 ? reviewButtonText : "Next"))
|
|
416
|
+
) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ReviewStateComponent, { formState, schemas: steps }), /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
|
|
417
|
+
Button,
|
|
418
|
+
{
|
|
419
|
+
onClick: handleBack,
|
|
420
|
+
className: styles.backButton,
|
|
421
|
+
disabled: activeStep < 1
|
|
422
|
+
},
|
|
423
|
+
"Back"
|
|
424
|
+
), /* @__PURE__ */ React.createElement(
|
|
425
|
+
Button,
|
|
426
|
+
{
|
|
427
|
+
variant: "contained",
|
|
428
|
+
onClick: () => {
|
|
429
|
+
var _a2;
|
|
430
|
+
props.onCreate(formState);
|
|
431
|
+
const name = typeof formState.name === "string" ? formState.name : void 0;
|
|
432
|
+
analytics.captureEvent(
|
|
433
|
+
"create",
|
|
434
|
+
(_a2 = name != null ? name : props.templateName) != null ? _a2 : "unknown"
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
createButtonText
|
|
439
|
+
)))));
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const useStyles$6 = makeStyles(
|
|
443
|
+
() => ({
|
|
444
|
+
header: {
|
|
445
|
+
backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage
|
|
446
|
+
},
|
|
447
|
+
subtitleWrapper: {
|
|
448
|
+
display: "flex",
|
|
449
|
+
justifyContent: "space-between"
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
);
|
|
453
|
+
const CardHeader = (props) => {
|
|
454
|
+
const {
|
|
455
|
+
template: {
|
|
456
|
+
metadata: { title, name },
|
|
457
|
+
spec: { type }
|
|
458
|
+
}
|
|
459
|
+
} = props;
|
|
460
|
+
const { getPageTheme } = useTheme();
|
|
461
|
+
const themeForType = getPageTheme({ themeId: type });
|
|
462
|
+
const styles = useStyles$6({
|
|
463
|
+
cardBackgroundImage: themeForType.backgroundImage
|
|
464
|
+
});
|
|
465
|
+
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 } })));
|
|
466
|
+
return /* @__PURE__ */ React.createElement(
|
|
467
|
+
ItemCardHeader,
|
|
468
|
+
{
|
|
469
|
+
title: title != null ? title : name,
|
|
470
|
+
subtitle: SubtitleComponent,
|
|
471
|
+
classes: { root: styles.header }
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const useStyles$5 = makeStyles(() => ({
|
|
477
|
+
linkText: {
|
|
478
|
+
display: "inline-flex",
|
|
479
|
+
alignItems: "center"
|
|
480
|
+
}
|
|
481
|
+
}));
|
|
482
|
+
const CardLink = ({ icon: Icon, text, url }) => {
|
|
483
|
+
const styles = useStyles$5();
|
|
484
|
+
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));
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
const useStyles$4 = makeStyles((theme) => ({
|
|
488
|
+
box: {
|
|
489
|
+
overflow: "hidden",
|
|
490
|
+
textOverflow: "ellipsis",
|
|
491
|
+
display: "-webkit-box",
|
|
492
|
+
"-webkit-line-clamp": 10,
|
|
493
|
+
"-webkit-box-orient": "vertical"
|
|
494
|
+
},
|
|
495
|
+
markdown: {
|
|
496
|
+
/** to make the styles for React Markdown not leak into the description */
|
|
497
|
+
"& :first-child": {
|
|
498
|
+
margin: 0
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
label: {
|
|
502
|
+
color: theme.palette.text.secondary,
|
|
503
|
+
textTransform: "uppercase",
|
|
504
|
+
fontWeight: "bold",
|
|
505
|
+
letterSpacing: 0.5,
|
|
506
|
+
lineHeight: 1,
|
|
507
|
+
fontSize: "0.75rem"
|
|
508
|
+
},
|
|
509
|
+
footer: {
|
|
510
|
+
display: "flex",
|
|
511
|
+
justifyContent: "space-between",
|
|
512
|
+
flex: 1,
|
|
513
|
+
alignItems: "center"
|
|
514
|
+
},
|
|
515
|
+
ownedBy: {
|
|
516
|
+
display: "flex",
|
|
517
|
+
alignItems: "center",
|
|
518
|
+
flex: 1,
|
|
519
|
+
color: theme.palette.link
|
|
520
|
+
}
|
|
521
|
+
}));
|
|
522
|
+
const TemplateCard = (props) => {
|
|
523
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
524
|
+
const { template } = props;
|
|
525
|
+
const styles = useStyles$4();
|
|
526
|
+
const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
|
|
527
|
+
const app = useApp();
|
|
528
|
+
const iconResolver = (key) => {
|
|
529
|
+
var _a2;
|
|
530
|
+
return key ? (_a2 = app.getSystemIcon(key)) != null ? _a2 : LanguageIcon : LanguageIcon;
|
|
531
|
+
};
|
|
532
|
+
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(
|
|
533
|
+
MarkdownContent,
|
|
534
|
+
{
|
|
535
|
+
className: styles.markdown,
|
|
536
|
+
content: (_a = template.metadata.description) != null ? _a : "No description"
|
|
537
|
+
}
|
|
538
|
+
))), ((_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(
|
|
539
|
+
Chip,
|
|
540
|
+
{
|
|
541
|
+
style: { margin: 0 },
|
|
542
|
+
size: "small",
|
|
543
|
+
label: tag,
|
|
544
|
+
key: tag
|
|
545
|
+
}
|
|
546
|
+
)))))), (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(
|
|
547
|
+
CardLink,
|
|
548
|
+
{
|
|
549
|
+
icon: iconResolver(icon),
|
|
550
|
+
text: title || url,
|
|
551
|
+
url
|
|
552
|
+
}
|
|
553
|
+
)))))))), /* @__PURE__ */ React.createElement(CardActions, { style: { padding: "16px", flex: 1, alignItems: "flex-end" } }, /* @__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(
|
|
554
|
+
EntityRefLinks,
|
|
555
|
+
{
|
|
556
|
+
style: { marginLeft: "8px" },
|
|
557
|
+
entityRefs: ownedByRelations,
|
|
558
|
+
defaultKind: "Group"
|
|
559
|
+
}
|
|
560
|
+
))), /* @__PURE__ */ React.createElement(
|
|
561
|
+
Button,
|
|
562
|
+
{
|
|
563
|
+
size: "small",
|
|
564
|
+
variant: "outlined",
|
|
565
|
+
color: "primary",
|
|
566
|
+
onClick: () => {
|
|
567
|
+
var _a2;
|
|
568
|
+
return (_a2 = props.onSelected) == null ? void 0 : _a2.call(props, template);
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
"Choose"
|
|
572
|
+
))));
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const TemplateGroup = (props) => {
|
|
576
|
+
const {
|
|
577
|
+
templates,
|
|
578
|
+
title,
|
|
579
|
+
components: { CardComponent } = {},
|
|
580
|
+
onSelected
|
|
581
|
+
} = props;
|
|
582
|
+
const titleComponent = typeof title === "string" ? /* @__PURE__ */ React.createElement(ContentHeader, { title }) : title;
|
|
583
|
+
if (templates.length === 0) {
|
|
584
|
+
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"), "."));
|
|
585
|
+
}
|
|
586
|
+
const Card = CardComponent || TemplateCard;
|
|
587
|
+
return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(ItemCardGrid, null, templates.map(({ template, additionalLinks }) => /* @__PURE__ */ React.createElement(
|
|
588
|
+
Card,
|
|
589
|
+
{
|
|
590
|
+
key: stringifyEntityRef(template),
|
|
591
|
+
additionalLinks,
|
|
592
|
+
template,
|
|
593
|
+
onSelected
|
|
594
|
+
}
|
|
595
|
+
))));
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const SecretsContext = createVersionedContext("secrets-context");
|
|
599
|
+
const SecretsContextProvider = ({ children }) => {
|
|
600
|
+
const [secrets, setSecrets] = useState({});
|
|
601
|
+
return /* @__PURE__ */ React.createElement(
|
|
602
|
+
SecretsContext.Provider,
|
|
603
|
+
{
|
|
604
|
+
value: createVersionedValueMap({ 1: { secrets, setSecrets } })
|
|
605
|
+
},
|
|
606
|
+
children
|
|
607
|
+
);
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
const useStyles$3 = makeStyles(() => ({
|
|
611
|
+
markdown: {
|
|
612
|
+
/** to make the styles for React Markdown not leak into the description */
|
|
613
|
+
"& :first-child": {
|
|
614
|
+
marginTop: 0
|
|
615
|
+
},
|
|
616
|
+
"& :last-child": {
|
|
617
|
+
marginBottom: 0
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}));
|
|
621
|
+
const Workflow = (workflowProps) => {
|
|
622
|
+
var _a;
|
|
623
|
+
const { title, description, namespace, templateName, ...props } = workflowProps;
|
|
624
|
+
const styles = useStyles$3();
|
|
625
|
+
const templateRef = stringifyEntityRef({
|
|
626
|
+
kind: "Template",
|
|
627
|
+
namespace,
|
|
628
|
+
name: templateName
|
|
629
|
+
});
|
|
630
|
+
const errorApi = useApi(errorApiRef);
|
|
631
|
+
const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
|
|
632
|
+
useEffect(() => {
|
|
633
|
+
if (error) {
|
|
634
|
+
errorApi.post(new Error(`Failed to load template, ${error}`));
|
|
635
|
+
}
|
|
636
|
+
}, [error, errorApi]);
|
|
637
|
+
if (error) {
|
|
638
|
+
return props.onError(error);
|
|
639
|
+
}
|
|
640
|
+
return /* @__PURE__ */ React.createElement(Content, null, loading && /* @__PURE__ */ React.createElement(Progress, null), manifest && /* @__PURE__ */ React.createElement(
|
|
641
|
+
InfoCard,
|
|
642
|
+
{
|
|
643
|
+
title: title != null ? title : manifest.title,
|
|
644
|
+
subheader: /* @__PURE__ */ React.createElement(
|
|
645
|
+
MarkdownContent,
|
|
646
|
+
{
|
|
647
|
+
className: styles.markdown,
|
|
648
|
+
content: (_a = description != null ? description : manifest.description) != null ? _a : "No description"
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
noPadding: true,
|
|
652
|
+
titleTypographyProps: { component: "h2" }
|
|
653
|
+
},
|
|
654
|
+
/* @__PURE__ */ React.createElement(Stepper, { manifest, ...props })
|
|
655
|
+
));
|
|
656
|
+
};
|
|
657
|
+
const EmbeddableWorkflow = (props) => /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(Workflow, { ...props }));
|
|
658
|
+
|
|
659
|
+
const useStyles$2 = makeStyles({
|
|
660
|
+
root: {
|
|
661
|
+
"&:hover": {
|
|
662
|
+
textDecoration: "none"
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
const LinkOutputs = (props) => {
|
|
667
|
+
const { links = [] } = props.output;
|
|
668
|
+
const classes = useStyles$2();
|
|
669
|
+
const app = useApp();
|
|
670
|
+
const entityRoute = useRouteRef(entityRouteRef);
|
|
671
|
+
const iconResolver = (key) => {
|
|
672
|
+
var _a;
|
|
673
|
+
return (_a = app.getSystemIcon(key)) != null ? _a : WebIcon;
|
|
674
|
+
};
|
|
675
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
|
|
676
|
+
if (entityRef) {
|
|
677
|
+
const entityName = parseEntityRef(entityRef);
|
|
678
|
+
const target = entityRoute(entityName);
|
|
679
|
+
return { title, icon, url: target };
|
|
680
|
+
}
|
|
681
|
+
return { title, icon, url };
|
|
682
|
+
}).map(({ url, title, icon }, i) => {
|
|
683
|
+
const Icon = iconResolver(icon);
|
|
684
|
+
return /* @__PURE__ */ React.createElement(Link, { to: url, key: i, classes: { root: classes.root } }, /* @__PURE__ */ React.createElement(Button, { startIcon: /* @__PURE__ */ React.createElement(Icon, null), component: "div", color: "primary" }, title));
|
|
685
|
+
}));
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
const DefaultTemplateOutputs = (props) => {
|
|
689
|
+
var _a;
|
|
690
|
+
if (!((_a = props.output) == null ? void 0 : _a.links)) {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
return /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(Box, { padding: 2, justifyContent: "center", display: "flex", gridGap: 16 }, /* @__PURE__ */ React.createElement(LinkOutputs, { output: props.output }))));
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
const useStepIconStyles = makeStyles((theme) => ({
|
|
697
|
+
root: {
|
|
698
|
+
color: theme.palette.text.disabled
|
|
699
|
+
},
|
|
700
|
+
completed: {
|
|
701
|
+
color: theme.palette.status.ok
|
|
702
|
+
},
|
|
703
|
+
error: {
|
|
704
|
+
color: theme.palette.status.error
|
|
705
|
+
}
|
|
706
|
+
}));
|
|
707
|
+
const StepIcon = (props) => {
|
|
708
|
+
const classes = useStepIconStyles();
|
|
709
|
+
const { active, completed, error, skipped } = props;
|
|
710
|
+
const getMiddle = () => {
|
|
711
|
+
if (active) {
|
|
712
|
+
return /* @__PURE__ */ React.createElement(CircularProgress, { size: "20px" });
|
|
713
|
+
}
|
|
714
|
+
if (completed) {
|
|
715
|
+
return /* @__PURE__ */ React.createElement(CheckCircleOutline, null);
|
|
716
|
+
}
|
|
717
|
+
if (error) {
|
|
718
|
+
return /* @__PURE__ */ React.createElement(ErrorOutline, null);
|
|
719
|
+
}
|
|
720
|
+
if (skipped) {
|
|
721
|
+
return /* @__PURE__ */ React.createElement(RemoveCircleOutline, null);
|
|
722
|
+
}
|
|
723
|
+
return /* @__PURE__ */ React.createElement(PanoramaFishEyeIcon, null);
|
|
724
|
+
};
|
|
725
|
+
return /* @__PURE__ */ React.createElement(
|
|
726
|
+
"div",
|
|
727
|
+
{
|
|
728
|
+
className: classNames(classes.root, {
|
|
729
|
+
[classes.completed]: completed,
|
|
730
|
+
[classes.error]: error
|
|
731
|
+
})
|
|
732
|
+
},
|
|
733
|
+
getMiddle()
|
|
734
|
+
);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
const StepTime = (props) => {
|
|
738
|
+
const [time, setTime] = useState("");
|
|
739
|
+
const { step } = props;
|
|
740
|
+
const calculate = useCallback(() => {
|
|
741
|
+
if (!step.startedAt) {
|
|
742
|
+
setTime("");
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
|
|
746
|
+
const startedAt = DateTime.fromISO(step.startedAt);
|
|
747
|
+
const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
|
|
748
|
+
setTime(humanizeDuration(formatted, { round: true }));
|
|
749
|
+
}, [step.endedAt, step.startedAt]);
|
|
750
|
+
useMountEffect(() => calculate());
|
|
751
|
+
useInterval(() => !step.endedAt && calculate(), 1e3);
|
|
752
|
+
return /* @__PURE__ */ React.createElement(Typography, { variant: "caption" }, time);
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const useStyles$1 = makeStyles((theme) => ({
|
|
756
|
+
failed: {
|
|
757
|
+
backgroundColor: theme.palette.error.main
|
|
758
|
+
},
|
|
759
|
+
success: {
|
|
760
|
+
backgroundColor: theme.palette.success.main
|
|
761
|
+
}
|
|
762
|
+
}));
|
|
763
|
+
const TaskBorder = (props) => {
|
|
764
|
+
const styles = useStyles$1();
|
|
765
|
+
if (!props.isComplete) {
|
|
766
|
+
return /* @__PURE__ */ React.createElement(LinearProgress, { variant: "indeterminate" });
|
|
767
|
+
}
|
|
768
|
+
return /* @__PURE__ */ React.createElement(
|
|
769
|
+
LinearProgress,
|
|
770
|
+
{
|
|
771
|
+
variant: "determinate",
|
|
772
|
+
classes: { bar: props.isError ? styles.failed : styles.success },
|
|
773
|
+
value: 100
|
|
774
|
+
}
|
|
775
|
+
);
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
const TaskSteps = (props) => {
|
|
779
|
+
var _a, _b;
|
|
780
|
+
return /* @__PURE__ */ React.createElement(Paper, { style: { position: "relative", overflow: "hidden" } }, /* @__PURE__ */ React.createElement(
|
|
781
|
+
TaskBorder,
|
|
782
|
+
{
|
|
783
|
+
isComplete: (_a = props.isComplete) != null ? _a : false,
|
|
784
|
+
isError: (_b = props.isError) != null ? _b : false
|
|
785
|
+
}
|
|
786
|
+
), /* @__PURE__ */ React.createElement(Box, { padding: 2 }, /* @__PURE__ */ React.createElement(
|
|
787
|
+
Stepper$1,
|
|
788
|
+
{
|
|
789
|
+
activeStep: props.activeStep,
|
|
790
|
+
alternativeLabel: true,
|
|
791
|
+
variant: "elevation"
|
|
792
|
+
},
|
|
793
|
+
props.steps.map((step, index) => {
|
|
794
|
+
const isCompleted = step.status === "completed";
|
|
795
|
+
const isFailed = step.status === "failed";
|
|
796
|
+
const isActive = step.status === "processing";
|
|
797
|
+
const isSkipped = step.status === "skipped";
|
|
798
|
+
const stepIconProps = {
|
|
799
|
+
completed: isCompleted,
|
|
800
|
+
error: isFailed,
|
|
801
|
+
active: isActive,
|
|
802
|
+
skipped: isSkipped
|
|
803
|
+
};
|
|
804
|
+
return /* @__PURE__ */ React.createElement(Step, { key: index }, /* @__PURE__ */ React.createElement(StepButton, null, /* @__PURE__ */ React.createElement(
|
|
805
|
+
StepLabel,
|
|
806
|
+
{
|
|
807
|
+
StepIconProps: stepIconProps,
|
|
808
|
+
StepIconComponent: StepIcon
|
|
809
|
+
},
|
|
810
|
+
/* @__PURE__ */ React.createElement(Box, null, step.name),
|
|
811
|
+
/* @__PURE__ */ React.createElement(StepTime, { step })
|
|
812
|
+
)));
|
|
813
|
+
})
|
|
814
|
+
)));
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
const useStyles = makeStyles$1({
|
|
818
|
+
root: {
|
|
819
|
+
width: "100%",
|
|
820
|
+
height: "100%",
|
|
821
|
+
position: "relative"
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
const TaskLogStream = (props) => {
|
|
825
|
+
const styles = useStyles();
|
|
826
|
+
return /* @__PURE__ */ React.createElement("div", { className: styles.root }, /* @__PURE__ */ React.createElement(
|
|
827
|
+
LogViewer,
|
|
828
|
+
{
|
|
829
|
+
text: Object.values(props.logs).map((l) => l.join("\n")).filter(Boolean).join("\n")
|
|
830
|
+
}
|
|
831
|
+
));
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
|
|
835
|
+
|
|
836
|
+
function createNextScaffolderFieldExtension(options) {
|
|
837
|
+
return {
|
|
838
|
+
expose() {
|
|
839
|
+
const FieldExtensionDataHolder = () => null;
|
|
840
|
+
attachComponentData(
|
|
841
|
+
FieldExtensionDataHolder,
|
|
842
|
+
FIELD_EXTENSION_KEY,
|
|
843
|
+
options
|
|
844
|
+
);
|
|
845
|
+
return FieldExtensionDataHolder;
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
export { DefaultTemplateOutputs, EmbeddableWorkflow, Form, ReviewState, Stepper, TaskLogStream, TaskSteps, TemplateCard, TemplateGroup, Workflow, createFieldValidation, createNextScaffolderFieldExtension, extractSchemaFromStep, useFormDataFromQuery, useTemplateParameterSchema, useTemplateSchema };
|
|
851
|
+
//# sourceMappingURL=alpha.esm.js.map
|