@flowgram.ai/test-run-plugin 0.1.0-alpha.20
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/.eslintrc.cjs +17 -0
- package/.rush/temp/chunked-rush-logs/test-run-plugin.build.chunks.jsonl +21 -0
- package/.rush/temp/package-deps_build.json +47 -0
- package/.rush/temp/shrinkwrap-deps.json +161 -0
- package/dist/esm/index.js +731 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +314 -0
- package/dist/index.d.ts +314 -0
- package/dist/index.js +770 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
- package/rush-logs/test-run-plugin.build.log +21 -0
- package/src/create-test-run-plugin.ts +39 -0
- package/src/form-engine/contexts.ts +16 -0
- package/src/form-engine/fields/create-field.tsx +32 -0
- package/src/form-engine/fields/general-field.tsx +35 -0
- package/src/form-engine/fields/index.ts +9 -0
- package/src/form-engine/fields/object-field.tsx +21 -0
- package/src/form-engine/fields/reactive-field.tsx +57 -0
- package/src/form-engine/fields/recursion-field.tsx +31 -0
- package/src/form-engine/fields/schema-field.tsx +32 -0
- package/src/form-engine/form/form.tsx +38 -0
- package/src/form-engine/form/index.ts +6 -0
- package/src/form-engine/hooks/index.ts +8 -0
- package/src/form-engine/hooks/use-create-form.ts +69 -0
- package/src/form-engine/hooks/use-field.ts +13 -0
- package/src/form-engine/hooks/use-form.ts +13 -0
- package/src/form-engine/index.ts +19 -0
- package/src/form-engine/model/index.ts +89 -0
- package/src/form-engine/types.ts +56 -0
- package/src/form-engine/utils.ts +64 -0
- package/src/index.ts +22 -0
- package/src/reactive/hooks/index.ts +7 -0
- package/src/reactive/hooks/use-create-form.ts +90 -0
- package/src/reactive/hooks/use-test-run-service.ts +10 -0
- package/src/reactive/index.ts +6 -0
- package/src/services/config.ts +42 -0
- package/src/services/form/factory.ts +9 -0
- package/src/services/form/form.ts +78 -0
- package/src/services/form/index.ts +8 -0
- package/src/services/form/manager.ts +43 -0
- package/src/services/index.ts +14 -0
- package/src/services/pipeline/factory.ts +9 -0
- package/src/services/pipeline/index.ts +12 -0
- package/src/services/pipeline/pipeline.ts +143 -0
- package/src/services/pipeline/plugin.ts +11 -0
- package/src/services/pipeline/tap.ts +34 -0
- package/src/services/store.ts +27 -0
- package/src/services/test-run.ts +100 -0
- package/src/types.ts +29 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
// src/create-test-run-plugin.ts
|
|
14
|
+
import { definePluginCreator } from "@flowgram.ai/core";
|
|
15
|
+
|
|
16
|
+
// src/services/form/form.ts
|
|
17
|
+
import { createElement as createElement2 } from "react";
|
|
18
|
+
import { nanoid } from "nanoid";
|
|
19
|
+
import { injectable, inject } from "inversify";
|
|
20
|
+
import { Emitter } from "@flowgram.ai/utils";
|
|
21
|
+
|
|
22
|
+
// src/services/config.ts
|
|
23
|
+
var TestRunConfig = Symbol("TestRunConfig");
|
|
24
|
+
var defineConfig = (config) => {
|
|
25
|
+
const defaultConfig = {
|
|
26
|
+
components: {},
|
|
27
|
+
nodes: {},
|
|
28
|
+
plugins: []
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
...defaultConfig,
|
|
32
|
+
...config
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/form-engine/form/form.tsx
|
|
37
|
+
import { Form } from "@flowgram.ai/form";
|
|
38
|
+
|
|
39
|
+
// src/form-engine/hooks/use-create-form.ts
|
|
40
|
+
import { useEffect, useMemo } from "react";
|
|
41
|
+
import { createForm, ValidateTrigger } from "@flowgram.ai/form";
|
|
42
|
+
|
|
43
|
+
// src/form-engine/utils.ts
|
|
44
|
+
import { createElement } from "react";
|
|
45
|
+
var getUniqueFieldName = (...args) => args.filter((path) => path).join(".");
|
|
46
|
+
var mergeFieldPath = (path, name) => [...path || [], name].filter((i) => Boolean(i));
|
|
47
|
+
var createValidate = (schema) => {
|
|
48
|
+
const rules = {};
|
|
49
|
+
visit(schema);
|
|
50
|
+
return rules;
|
|
51
|
+
function visit(current, name) {
|
|
52
|
+
if (name && current["x-validator"]) {
|
|
53
|
+
rules[name] = current["x-validator"];
|
|
54
|
+
}
|
|
55
|
+
if (current.type === "object" && current.properties) {
|
|
56
|
+
Object.entries(current.properties).forEach(([key, value]) => {
|
|
57
|
+
visit(value, getUniqueFieldName(name, key));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var connect = (Component, mapProps) => {
|
|
63
|
+
const Connected = (props) => {
|
|
64
|
+
const mappedProps = mapProps(props);
|
|
65
|
+
return createElement(Component, mappedProps, mappedProps.children);
|
|
66
|
+
};
|
|
67
|
+
return Connected;
|
|
68
|
+
};
|
|
69
|
+
var isFormEmpty = (schema) => {
|
|
70
|
+
const isEmpty = (s) => {
|
|
71
|
+
if (!s.type || s.type === "object" || !s.name) {
|
|
72
|
+
return Object.entries(schema.properties || {}).map(([key, value]) => ({
|
|
73
|
+
name: key,
|
|
74
|
+
...value
|
|
75
|
+
})).every(isFormEmpty);
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
};
|
|
79
|
+
return isEmpty(schema);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/form-engine/model/index.ts
|
|
83
|
+
import { ReactiveState } from "@flowgram.ai/reactive";
|
|
84
|
+
var FormSchemaModel = class _FormSchemaModel {
|
|
85
|
+
constructor(json, path = []) {
|
|
86
|
+
this.path = [];
|
|
87
|
+
this.state = new ReactiveState({ disabled: false });
|
|
88
|
+
this.fromJSON(json);
|
|
89
|
+
this.path = path;
|
|
90
|
+
}
|
|
91
|
+
get componentType() {
|
|
92
|
+
return this["x-component"];
|
|
93
|
+
}
|
|
94
|
+
get componentProps() {
|
|
95
|
+
return this["x-component-props"];
|
|
96
|
+
}
|
|
97
|
+
get decoratorType() {
|
|
98
|
+
return this["x-decorator"];
|
|
99
|
+
}
|
|
100
|
+
get decoratorProps() {
|
|
101
|
+
return this["x-decorator-props"];
|
|
102
|
+
}
|
|
103
|
+
get uniqueName() {
|
|
104
|
+
return getUniqueFieldName(...this.path);
|
|
105
|
+
}
|
|
106
|
+
fromJSON(json) {
|
|
107
|
+
Object.entries(json).forEach(([key, value]) => {
|
|
108
|
+
this[key] = value;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
getPropertyList() {
|
|
112
|
+
const orderProperties = [];
|
|
113
|
+
const unOrderProperties = [];
|
|
114
|
+
Object.entries(this.properties || {}).forEach(([key, item]) => {
|
|
115
|
+
const index = item["x-index"];
|
|
116
|
+
const defaultValues = this.defaultValue;
|
|
117
|
+
if (typeof defaultValues === "object" && defaultValues !== null && key in defaultValues) {
|
|
118
|
+
item.defaultValue = defaultValues[key];
|
|
119
|
+
}
|
|
120
|
+
const current = new _FormSchemaModel(item, mergeFieldPath(this.path, key));
|
|
121
|
+
if (index !== void 0 && !isNaN(index)) {
|
|
122
|
+
orderProperties[index] = current;
|
|
123
|
+
} else {
|
|
124
|
+
unOrderProperties.push(current);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return orderProperties.concat(unOrderProperties).filter((item) => !!item);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/form-engine/hooks/use-create-form.ts
|
|
132
|
+
var useCreateForm = (schema, options = {}) => {
|
|
133
|
+
const { form, control } = useMemo(
|
|
134
|
+
() => createForm({
|
|
135
|
+
validate: {
|
|
136
|
+
...createValidate(schema),
|
|
137
|
+
...options.validate
|
|
138
|
+
},
|
|
139
|
+
validateTrigger: options.validateTrigger ?? ValidateTrigger.onBlur
|
|
140
|
+
}),
|
|
141
|
+
[schema]
|
|
142
|
+
);
|
|
143
|
+
const model = useMemo(
|
|
144
|
+
() => new FormSchemaModel({ type: "object", ...schema, defaultValue: options.defaultValues }),
|
|
145
|
+
[schema]
|
|
146
|
+
);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (options.onMounted) {
|
|
149
|
+
options.onMounted({ model, form });
|
|
150
|
+
}
|
|
151
|
+
const disposable = control._formModel.onFormValuesChange((payload) => {
|
|
152
|
+
if (options.onFormValuesChange) {
|
|
153
|
+
options.onFormValuesChange(payload);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return () => {
|
|
157
|
+
disposable.dispose();
|
|
158
|
+
if (options.onUnmounted) {
|
|
159
|
+
options.onUnmounted();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}, [control]);
|
|
163
|
+
return {
|
|
164
|
+
form,
|
|
165
|
+
control,
|
|
166
|
+
model
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/form-engine/hooks/use-field.ts
|
|
171
|
+
import { useContext } from "react";
|
|
172
|
+
import { useObserve } from "@flowgram.ai/reactive";
|
|
173
|
+
|
|
174
|
+
// src/form-engine/contexts.ts
|
|
175
|
+
import { createContext } from "react";
|
|
176
|
+
var FieldModelContext = createContext({});
|
|
177
|
+
var FormModelContext = createContext({});
|
|
178
|
+
var ComponentsContext = createContext({});
|
|
179
|
+
|
|
180
|
+
// src/form-engine/hooks/use-field.ts
|
|
181
|
+
var useFieldModel = () => useContext(FieldModelContext);
|
|
182
|
+
var useFieldState = () => useObserve(useFieldModel().state.value);
|
|
183
|
+
|
|
184
|
+
// src/form-engine/hooks/use-form.ts
|
|
185
|
+
import { useContext as useContext2 } from "react";
|
|
186
|
+
import { useObserve as useObserve2 } from "@flowgram.ai/reactive";
|
|
187
|
+
var useFormModel = () => useContext2(FormModelContext);
|
|
188
|
+
var useFormState = () => useObserve2(useFormModel().state.value);
|
|
189
|
+
|
|
190
|
+
// src/form-engine/fields/schema-field.tsx
|
|
191
|
+
import { useState } from "react";
|
|
192
|
+
|
|
193
|
+
// src/form-engine/fields/recursion-field.tsx
|
|
194
|
+
import { useMemo as useMemo2 } from "react";
|
|
195
|
+
|
|
196
|
+
// src/form-engine/fields/reactive-field.tsx
|
|
197
|
+
import { useContext as useContext3 } from "react";
|
|
198
|
+
import React from "react";
|
|
199
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
200
|
+
var ReactiveField = (props) => {
|
|
201
|
+
const formState = useFormState();
|
|
202
|
+
const model = useFieldModel();
|
|
203
|
+
const modelState = useFieldState();
|
|
204
|
+
const components = useContext3(ComponentsContext);
|
|
205
|
+
const disabled = modelState.disabled || formState.disabled;
|
|
206
|
+
const componentRender = () => {
|
|
207
|
+
if (!model.componentType || !components[model.componentType]) {
|
|
208
|
+
return props.children;
|
|
209
|
+
}
|
|
210
|
+
return React.createElement(
|
|
211
|
+
components[model.componentType],
|
|
212
|
+
{
|
|
213
|
+
disabled,
|
|
214
|
+
...model.componentProps,
|
|
215
|
+
...props.componentProps
|
|
216
|
+
},
|
|
217
|
+
props.children
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
const decoratorRender = (children) => {
|
|
221
|
+
if (!model.decoratorType || !components[model.decoratorType]) {
|
|
222
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
223
|
+
}
|
|
224
|
+
return React.createElement(
|
|
225
|
+
components[model.decoratorType],
|
|
226
|
+
{
|
|
227
|
+
type: model.type,
|
|
228
|
+
required: model.required,
|
|
229
|
+
...model.decoratorProps,
|
|
230
|
+
...props.decoratorProps
|
|
231
|
+
},
|
|
232
|
+
children
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
return decoratorRender(componentRender());
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// src/form-engine/fields/object-field.tsx
|
|
239
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
240
|
+
var ObjectField = ({
|
|
241
|
+
model,
|
|
242
|
+
children
|
|
243
|
+
}) => /* @__PURE__ */ jsx2(FieldModelContext.Provider, { value: model, children: /* @__PURE__ */ jsx2(ReactiveField, { children }) });
|
|
244
|
+
|
|
245
|
+
// src/form-engine/fields/general-field.tsx
|
|
246
|
+
import { Field } from "@flowgram.ai/form";
|
|
247
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
248
|
+
var GeneralField = ({ model }) => /* @__PURE__ */ jsx3(FieldModelContext.Provider, { value: model, children: /* @__PURE__ */ jsx3(
|
|
249
|
+
Field,
|
|
250
|
+
{
|
|
251
|
+
name: model.uniqueName,
|
|
252
|
+
defaultValue: model.defaultValue,
|
|
253
|
+
render: ({ field, fieldState }) => /* @__PURE__ */ jsx3(
|
|
254
|
+
ReactiveField,
|
|
255
|
+
{
|
|
256
|
+
componentProps: {
|
|
257
|
+
value: field.value,
|
|
258
|
+
onChange: field.onChange,
|
|
259
|
+
onFocus: field.onFocus,
|
|
260
|
+
onBlur: field.onBlur,
|
|
261
|
+
...fieldState
|
|
262
|
+
},
|
|
263
|
+
decoratorProps: fieldState
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
) });
|
|
268
|
+
|
|
269
|
+
// src/form-engine/fields/recursion-field.tsx
|
|
270
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
271
|
+
var RecursionField = ({ model }) => {
|
|
272
|
+
const properties = useMemo2(() => model.getPropertyList(), [model]);
|
|
273
|
+
if (model.type !== "object") {
|
|
274
|
+
return /* @__PURE__ */ jsx4(GeneralField, { model });
|
|
275
|
+
}
|
|
276
|
+
return /* @__PURE__ */ jsx4(ObjectField, { model, children: properties.map((item) => /* @__PURE__ */ jsx4(RecursionField, { model: item }, item.uniqueName)) });
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// src/form-engine/fields/schema-field.tsx
|
|
280
|
+
import { jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
281
|
+
var SchemaField = ({
|
|
282
|
+
components,
|
|
283
|
+
model,
|
|
284
|
+
children
|
|
285
|
+
}) => {
|
|
286
|
+
const [innerComponents] = useState(() => components);
|
|
287
|
+
return /* @__PURE__ */ jsx5(ComponentsContext.Provider, { value: innerComponents, children: /* @__PURE__ */ jsxs(FormModelContext.Provider, { value: model, children: [
|
|
288
|
+
/* @__PURE__ */ jsx5(RecursionField, { model }),
|
|
289
|
+
children
|
|
290
|
+
] }) });
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// src/form-engine/fields/create-field.tsx
|
|
294
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
295
|
+
var createSchemaField = (options) => {
|
|
296
|
+
const InnerSchemaField = ({
|
|
297
|
+
components,
|
|
298
|
+
...props
|
|
299
|
+
}) => /* @__PURE__ */ jsx6(
|
|
300
|
+
SchemaField,
|
|
301
|
+
{
|
|
302
|
+
components: {
|
|
303
|
+
...options.components,
|
|
304
|
+
...components
|
|
305
|
+
},
|
|
306
|
+
...props
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
return InnerSchemaField;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/form-engine/form/form.tsx
|
|
313
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
314
|
+
var SchemaField2 = createSchemaField({});
|
|
315
|
+
var FormEngine = ({
|
|
316
|
+
schema,
|
|
317
|
+
components,
|
|
318
|
+
children,
|
|
319
|
+
...props
|
|
320
|
+
}) => {
|
|
321
|
+
const { model, control } = useCreateForm(schema, props);
|
|
322
|
+
return /* @__PURE__ */ jsx7(Form, { control, children: /* @__PURE__ */ jsx7(SchemaField2, { model, components, children }) });
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/services/form/form.ts
|
|
326
|
+
var TestRunFormEntity = class {
|
|
327
|
+
constructor() {
|
|
328
|
+
this.initialized = false;
|
|
329
|
+
this.id = nanoid();
|
|
330
|
+
this.form = null;
|
|
331
|
+
this.onFormMountedEmitter = new Emitter();
|
|
332
|
+
this.onFormMounted = this.onFormMountedEmitter.event;
|
|
333
|
+
this.onFormUnmountedEmitter = new Emitter();
|
|
334
|
+
this.onFormUnmounted = this.onFormUnmountedEmitter.event;
|
|
335
|
+
}
|
|
336
|
+
get schema() {
|
|
337
|
+
return this._schema;
|
|
338
|
+
}
|
|
339
|
+
init(options) {
|
|
340
|
+
if (this.initialized) return;
|
|
341
|
+
this._schema = options.schema;
|
|
342
|
+
this.initialized = true;
|
|
343
|
+
}
|
|
344
|
+
render(props) {
|
|
345
|
+
if (!this.initialized) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
const { children, ...restProps } = props || {};
|
|
349
|
+
return createElement2(
|
|
350
|
+
FormEngine,
|
|
351
|
+
{
|
|
352
|
+
schema: this.schema,
|
|
353
|
+
components: this.config.components,
|
|
354
|
+
onMounted: (instance) => {
|
|
355
|
+
this.form = instance;
|
|
356
|
+
this.onFormMountedEmitter.fire(instance);
|
|
357
|
+
},
|
|
358
|
+
onUnmounted: this.onFormUnmountedEmitter.fire.bind(this.onFormUnmountedEmitter),
|
|
359
|
+
...restProps
|
|
360
|
+
},
|
|
361
|
+
children
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
dispose() {
|
|
365
|
+
this._schema = {};
|
|
366
|
+
this.form = null;
|
|
367
|
+
this.onFormMountedEmitter.dispose();
|
|
368
|
+
this.onFormUnmountedEmitter.dispose();
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
__decorateClass([
|
|
372
|
+
inject(TestRunConfig)
|
|
373
|
+
], TestRunFormEntity.prototype, "config", 2);
|
|
374
|
+
TestRunFormEntity = __decorateClass([
|
|
375
|
+
injectable()
|
|
376
|
+
], TestRunFormEntity);
|
|
377
|
+
|
|
378
|
+
// src/services/form/factory.ts
|
|
379
|
+
var TestRunFormFactory = Symbol("TestRunFormFactory");
|
|
380
|
+
|
|
381
|
+
// src/services/form/manager.ts
|
|
382
|
+
import { inject as inject2, injectable as injectable2 } from "inversify";
|
|
383
|
+
var TestRunFormManager = class {
|
|
384
|
+
constructor() {
|
|
385
|
+
this.entities = /* @__PURE__ */ new Map();
|
|
386
|
+
}
|
|
387
|
+
createForm() {
|
|
388
|
+
return this.factory();
|
|
389
|
+
}
|
|
390
|
+
getForm(id) {
|
|
391
|
+
return this.entities.get(id);
|
|
392
|
+
}
|
|
393
|
+
getAllForm() {
|
|
394
|
+
return Array.from(this.entities);
|
|
395
|
+
}
|
|
396
|
+
disposeForm(id) {
|
|
397
|
+
const form = this.entities.get(id);
|
|
398
|
+
if (!form) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
form.dispose();
|
|
402
|
+
this.entities.delete(id);
|
|
403
|
+
}
|
|
404
|
+
disposeAllForm() {
|
|
405
|
+
for (const id of this.entities.keys()) {
|
|
406
|
+
this.disposeForm(id);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
__decorateClass([
|
|
411
|
+
inject2(TestRunFormFactory)
|
|
412
|
+
], TestRunFormManager.prototype, "factory", 2);
|
|
413
|
+
TestRunFormManager = __decorateClass([
|
|
414
|
+
injectable2()
|
|
415
|
+
], TestRunFormManager);
|
|
416
|
+
|
|
417
|
+
// src/services/test-run.ts
|
|
418
|
+
import { inject as inject3, injectable as injectable3 } from "inversify";
|
|
419
|
+
import { DisposableCollection, Emitter as Emitter2 } from "@flowgram.ai/utils";
|
|
420
|
+
|
|
421
|
+
// src/services/pipeline/factory.ts
|
|
422
|
+
var TestRunPipelineFactory = Symbol("TestRunPipelineFactory");
|
|
423
|
+
|
|
424
|
+
// src/services/test-run.ts
|
|
425
|
+
var TestRunService = class {
|
|
426
|
+
constructor() {
|
|
427
|
+
this.pipelineEntities = /* @__PURE__ */ new Map();
|
|
428
|
+
this.pipelineBindings = /* @__PURE__ */ new Map();
|
|
429
|
+
this.onPipelineProgressEmitter = new Emitter2();
|
|
430
|
+
this.onPipelineProgress = this.onPipelineProgressEmitter.event;
|
|
431
|
+
this.onPipelineFinishedEmitter = new Emitter2();
|
|
432
|
+
this.onPipelineFinished = this.onPipelineFinishedEmitter.event;
|
|
433
|
+
}
|
|
434
|
+
isEnabled(nodeType) {
|
|
435
|
+
const config = this.config.nodes[nodeType];
|
|
436
|
+
return config && config?.enabled !== false;
|
|
437
|
+
}
|
|
438
|
+
async toSchema(node) {
|
|
439
|
+
const nodeType = node.flowNodeType;
|
|
440
|
+
const config = this.config.nodes[nodeType];
|
|
441
|
+
if (!this.isEnabled(nodeType)) {
|
|
442
|
+
return {};
|
|
443
|
+
}
|
|
444
|
+
const properties = typeof config.properties === "function" ? await config.properties({ node }) : config.properties;
|
|
445
|
+
return {
|
|
446
|
+
type: "object",
|
|
447
|
+
properties
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
createFormWithSchema(schema) {
|
|
451
|
+
const form = this.formManager.createForm();
|
|
452
|
+
form.init({ schema });
|
|
453
|
+
return form;
|
|
454
|
+
}
|
|
455
|
+
async createForm(node) {
|
|
456
|
+
const schema = await this.toSchema(node);
|
|
457
|
+
return this.createFormWithSchema(schema);
|
|
458
|
+
}
|
|
459
|
+
createPipeline(options) {
|
|
460
|
+
const pipeline = this.pipelineFactory();
|
|
461
|
+
this.pipelineEntities.set(pipeline.id, pipeline);
|
|
462
|
+
pipeline.init(options);
|
|
463
|
+
return pipeline;
|
|
464
|
+
}
|
|
465
|
+
connectPipeline(pipeline) {
|
|
466
|
+
if (this.pipelineBindings.get(pipeline.id)) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const disposable = new DisposableCollection(
|
|
470
|
+
pipeline.onProgress(this.onPipelineProgressEmitter.fire.bind(this.onPipelineProgressEmitter)),
|
|
471
|
+
pipeline.onFinished(this.onPipelineFinishedEmitter.fire.bind(this.onPipelineFinishedEmitter))
|
|
472
|
+
);
|
|
473
|
+
this.pipelineBindings.set(pipeline.id, disposable);
|
|
474
|
+
}
|
|
475
|
+
disconnectPipeline(id) {
|
|
476
|
+
if (this.pipelineBindings.has(id)) {
|
|
477
|
+
const disposable = this.pipelineBindings.get(id);
|
|
478
|
+
disposable?.dispose();
|
|
479
|
+
this.pipelineBindings.delete(id);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
disconnectAllPipeline() {
|
|
483
|
+
for (const id of this.pipelineBindings.keys()) {
|
|
484
|
+
this.disconnectPipeline(id);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
__decorateClass([
|
|
489
|
+
inject3(TestRunConfig)
|
|
490
|
+
], TestRunService.prototype, "config", 2);
|
|
491
|
+
__decorateClass([
|
|
492
|
+
inject3(TestRunPipelineFactory)
|
|
493
|
+
], TestRunService.prototype, "pipelineFactory", 2);
|
|
494
|
+
__decorateClass([
|
|
495
|
+
inject3(TestRunFormManager)
|
|
496
|
+
], TestRunService.prototype, "formManager", 2);
|
|
497
|
+
TestRunService = __decorateClass([
|
|
498
|
+
injectable3()
|
|
499
|
+
], TestRunService);
|
|
500
|
+
|
|
501
|
+
// src/services/pipeline/pipeline.ts
|
|
502
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
503
|
+
import { injectable as injectable5 } from "inversify";
|
|
504
|
+
import { Emitter as Emitter3 } from "@flowgram.ai/utils";
|
|
505
|
+
|
|
506
|
+
// src/services/pipeline/tap.ts
|
|
507
|
+
var Tap = class {
|
|
508
|
+
constructor() {
|
|
509
|
+
this.taps = [];
|
|
510
|
+
this.frozen = false;
|
|
511
|
+
}
|
|
512
|
+
tap(name, fn) {
|
|
513
|
+
this.taps.push({ name, fn });
|
|
514
|
+
}
|
|
515
|
+
async call(ctx) {
|
|
516
|
+
for (const tap of this.taps) {
|
|
517
|
+
if (this.frozen) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
await tap.fn(ctx);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
freeze() {
|
|
524
|
+
this.frozen = true;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// src/services/store.ts
|
|
529
|
+
import { createStore } from "zustand/vanilla";
|
|
530
|
+
import { injectable as injectable4, unmanaged } from "inversify";
|
|
531
|
+
var StoreService = class {
|
|
532
|
+
get getState() {
|
|
533
|
+
return this.store.getState.bind(this.store);
|
|
534
|
+
}
|
|
535
|
+
get setState() {
|
|
536
|
+
return this.store.setState.bind(this.store);
|
|
537
|
+
}
|
|
538
|
+
constructor(stateCreator) {
|
|
539
|
+
this.store = createStore(stateCreator);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
StoreService = __decorateClass([
|
|
543
|
+
injectable4(),
|
|
544
|
+
__decorateParam(0, unmanaged())
|
|
545
|
+
], StoreService);
|
|
546
|
+
|
|
547
|
+
// src/services/pipeline/pipeline.ts
|
|
548
|
+
var initialState = {
|
|
549
|
+
status: "idle",
|
|
550
|
+
data: {}
|
|
551
|
+
};
|
|
552
|
+
var TestRunPipelineEntity = class extends StoreService {
|
|
553
|
+
constructor() {
|
|
554
|
+
super((set, get) => ({
|
|
555
|
+
...initialState,
|
|
556
|
+
getData: () => get().data || {},
|
|
557
|
+
setData: (next) => set((state) => ({ ...state, data: { ...state.data, ...next } }))
|
|
558
|
+
}));
|
|
559
|
+
this.id = nanoid2();
|
|
560
|
+
this.prepare = new Tap();
|
|
561
|
+
this.onProgressEmitter = new Emitter3();
|
|
562
|
+
this.onProgress = this.onProgressEmitter.event;
|
|
563
|
+
this.onFinishedEmitter = new Emitter3();
|
|
564
|
+
this.onFinished = this.onFinishedEmitter.event;
|
|
565
|
+
}
|
|
566
|
+
get status() {
|
|
567
|
+
return this.getState().status;
|
|
568
|
+
}
|
|
569
|
+
set status(next) {
|
|
570
|
+
this.setState({ status: next });
|
|
571
|
+
}
|
|
572
|
+
init(options) {
|
|
573
|
+
if (!this.container) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const { plugins } = options;
|
|
577
|
+
for (const PluginClass of plugins) {
|
|
578
|
+
const plugin = this.container.resolve(PluginClass);
|
|
579
|
+
plugin.apply(this);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
registerExecute(fn) {
|
|
583
|
+
this.execute = fn;
|
|
584
|
+
}
|
|
585
|
+
registerProgress(fn) {
|
|
586
|
+
this.progress = fn;
|
|
587
|
+
}
|
|
588
|
+
async start(options) {
|
|
589
|
+
const { data } = options || {};
|
|
590
|
+
if (this.status !== "idle") {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
this.setState({ data });
|
|
594
|
+
const ctx = {
|
|
595
|
+
id: this.id,
|
|
596
|
+
store: this.store,
|
|
597
|
+
operate: {
|
|
598
|
+
update: this.update.bind(this),
|
|
599
|
+
cancel: this.cancel.bind(this)
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
this.status = "preparing";
|
|
603
|
+
await this.prepare.call(ctx);
|
|
604
|
+
if (this.status !== "preparing") {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
this.status = "executing";
|
|
608
|
+
if (this.execute) {
|
|
609
|
+
await this.execute(ctx);
|
|
610
|
+
}
|
|
611
|
+
if (this.progress) {
|
|
612
|
+
await this.progress(ctx);
|
|
613
|
+
}
|
|
614
|
+
if (this.status === "executing") {
|
|
615
|
+
this.status = "finished";
|
|
616
|
+
this.onFinishedEmitter.fire(this.getState().result);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
update(result) {
|
|
620
|
+
this.setState({ result });
|
|
621
|
+
this.onProgressEmitter.fire(result);
|
|
622
|
+
}
|
|
623
|
+
cancel() {
|
|
624
|
+
if (this.status = "preparing") {
|
|
625
|
+
this.prepare.freeze();
|
|
626
|
+
}
|
|
627
|
+
this.status = "canceled";
|
|
628
|
+
}
|
|
629
|
+
dispose() {
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
TestRunPipelineEntity = __decorateClass([
|
|
633
|
+
injectable5()
|
|
634
|
+
], TestRunPipelineEntity);
|
|
635
|
+
|
|
636
|
+
// src/create-test-run-plugin.ts
|
|
637
|
+
var createTestRunPlugin = definePluginCreator({
|
|
638
|
+
onBind: ({ bind }, opt) => {
|
|
639
|
+
bind(TestRunService).toSelf().inSingletonScope();
|
|
640
|
+
bind(TestRunConfig).toConstantValue(defineConfig(opt));
|
|
641
|
+
bind(TestRunFormManager).toSelf().inSingletonScope();
|
|
642
|
+
bind(TestRunFormFactory).toFactory((context) => () => {
|
|
643
|
+
const e = context.container.resolve(TestRunFormEntity);
|
|
644
|
+
return e;
|
|
645
|
+
});
|
|
646
|
+
bind(TestRunPipelineFactory).toFactory(
|
|
647
|
+
(context) => () => {
|
|
648
|
+
const e = context.container.resolve(TestRunPipelineEntity);
|
|
649
|
+
e.container = context.container.createChild();
|
|
650
|
+
return e;
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
// src/reactive/hooks/use-create-form.ts
|
|
657
|
+
import { useEffect as useEffect2, useMemo as useMemo3, useState as useState2 } from "react";
|
|
658
|
+
import { DisposableCollection as DisposableCollection2 } from "@flowgram.ai/utils";
|
|
659
|
+
|
|
660
|
+
// src/reactive/hooks/use-test-run-service.ts
|
|
661
|
+
import { useService } from "@flowgram.ai/core";
|
|
662
|
+
var useTestRunService = () => useService(TestRunService);
|
|
663
|
+
|
|
664
|
+
// src/reactive/hooks/use-create-form.ts
|
|
665
|
+
var useCreateForm2 = ({
|
|
666
|
+
node,
|
|
667
|
+
loadingRenderer,
|
|
668
|
+
emptyRenderer,
|
|
669
|
+
defaultValues,
|
|
670
|
+
onMounted,
|
|
671
|
+
onUnmounted,
|
|
672
|
+
onFormValuesChange
|
|
673
|
+
}) => {
|
|
674
|
+
const testRun = useTestRunService();
|
|
675
|
+
const [loading, setLoading] = useState2(false);
|
|
676
|
+
const [form, setForm] = useState2(null);
|
|
677
|
+
const renderer = useMemo3(() => {
|
|
678
|
+
if (loading || !form) {
|
|
679
|
+
return loadingRenderer;
|
|
680
|
+
}
|
|
681
|
+
const isEmpty = isFormEmpty(form.schema);
|
|
682
|
+
return form.render({
|
|
683
|
+
defaultValues,
|
|
684
|
+
onFormValuesChange,
|
|
685
|
+
children: isEmpty ? emptyRenderer : null
|
|
686
|
+
});
|
|
687
|
+
}, [form, loading]);
|
|
688
|
+
const compute = async () => {
|
|
689
|
+
if (!node) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
try {
|
|
693
|
+
setLoading(true);
|
|
694
|
+
const formEntity = await testRun.createForm(node);
|
|
695
|
+
setForm(formEntity);
|
|
696
|
+
} finally {
|
|
697
|
+
setLoading(false);
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
useEffect2(() => {
|
|
701
|
+
compute();
|
|
702
|
+
}, [node]);
|
|
703
|
+
useEffect2(() => {
|
|
704
|
+
if (!form) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const disposable = new DisposableCollection2(
|
|
708
|
+
form.onFormMounted((data) => {
|
|
709
|
+
onMounted?.(data);
|
|
710
|
+
}),
|
|
711
|
+
form.onFormUnmounted(() => {
|
|
712
|
+
onUnmounted?.();
|
|
713
|
+
})
|
|
714
|
+
);
|
|
715
|
+
return () => disposable.dispose();
|
|
716
|
+
}, [form]);
|
|
717
|
+
return {
|
|
718
|
+
renderer,
|
|
719
|
+
loading,
|
|
720
|
+
form
|
|
721
|
+
};
|
|
722
|
+
};
|
|
723
|
+
export {
|
|
724
|
+
FormEngine,
|
|
725
|
+
TestRunPipelineEntity,
|
|
726
|
+
connect,
|
|
727
|
+
createTestRunPlugin,
|
|
728
|
+
useCreateForm2 as useCreateForm,
|
|
729
|
+
useTestRunService
|
|
730
|
+
};
|
|
731
|
+
//# sourceMappingURL=index.js.map
|