@qoretechnologies/reqraft 0.10.53 → 0.10.54-pr.115.ge2d364c
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/.claude/CLAUDE.md +25 -0
- package/.tasks/INDEX.md +1 -0
- package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
- package/README.md +1 -1
- package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
- package/design/RENDER_EXPRESSION_TRANSPORT.md +18 -0
- package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
- package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
- package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/index.d.ts +4 -0
- package/dist/components/dpqlEditor/index.d.ts.map +1 -1
- package/dist/components/dpqlEditor/index.js +5 -1
- package/dist/components/dpqlEditor/index.js.map +1 -1
- package/dist/components/dpqlEditor/types.d.ts +29 -2
- package/dist/components/dpqlEditor/types.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
- package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
- package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +186 -21
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +5 -2
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +159 -11
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +376 -75
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js +38 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
- package/dist/components/form/engine/OptionFieldMessages.d.ts +17 -2
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +61 -11
- package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js +7 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.d.ts +42 -1
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +74 -7
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +13 -0
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
- package/dist/components/form/engine/rendererTypes.js.map +1 -1
- package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
- package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
- package/dist/components/form/engine/rowMenuContext.js +103 -0
- package/dist/components/form/engine/rowMenuContext.js.map +1 -0
- package/dist/components/form/engine/typeChoices.d.ts +37 -0
- package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
- package/dist/components/form/engine/typeChoices.js +25 -0
- package/dist/components/form/engine/typeChoices.js.map +1 -0
- package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
- package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
- package/dist/components/form/expressions/ExpressionField.js +181 -33
- package/dist/components/form/expressions/ExpressionField.js.map +1 -1
- package/dist/components/form/expressions/argumentPresence.d.ts +97 -0
- package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
- package/dist/components/form/expressions/argumentPresence.js +203 -0
- package/dist/components/form/expressions/argumentPresence.js.map +1 -0
- package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
- package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
- package/dist/components/form/expressions/builder/index.d.ts +15 -2
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +19 -57
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/dpqlMockLsp.d.ts +17 -0
- package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +1 -1
- package/dist/components/form/expressions/dpqlMockLsp.js +75 -9
- package/dist/components/form/expressions/dpqlMockLsp.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +94 -19
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +3 -1
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +14 -2
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +13 -2
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts +11 -0
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +84 -5
- package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
- package/dist/components/form/fields/template/TemplateField.d.ts +31 -1
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +507 -40
- package/dist/components/form/fields/template/TemplateField.js.map +1 -1
- package/dist/components/form/index.d.ts +1 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +1 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +1 -0
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +1 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js +6 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +1 -1
- package/dist/helpers/dpqlDetection.d.ts +58 -0
- package/dist/helpers/dpqlDetection.d.ts.map +1 -0
- package/dist/helpers/dpqlDetection.js +164 -0
- package/dist/helpers/dpqlDetection.js.map +1 -0
- package/dist/helpers/optionUiTypes.d.ts +1 -1
- package/dist/helpers/options.d.ts +37 -0
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +78 -1
- package/dist/helpers/options.js.map +1 -1
- package/dist/helpers/templateItems.d.ts +47 -0
- package/dist/helpers/templateItems.d.ts.map +1 -0
- package/dist/helpers/templateItems.js +88 -0
- package/dist/helpers/templateItems.js.map +1 -0
- package/dist/helpers/templates.d.ts +21 -0
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +49 -3
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +56 -9
- package/dist/helpers/validations.js.map +1 -1
- package/dist/hooks/useArgSchema.d.ts +2 -1
- package/dist/hooks/useArgSchema.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.d.ts +3 -1
- package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.js +47 -9
- package/dist/hooks/useFetch/useFetch.js.map +1 -1
- package/dist/hooks/useTemplates.d.ts +20 -0
- package/dist/hooks/useTemplates.d.ts.map +1 -1
- package/dist/hooks/useTemplates.js +34 -2
- package/dist/hooks/useTemplates.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/stories/storyNetwork.d.ts +36 -0
- package/dist/stories/storyNetwork.d.ts.map +1 -0
- package/dist/stories/storyNetwork.js +82 -0
- package/dist/stories/storyNetwork.js.map +1 -0
- package/dist/utils/fetch.d.ts +151 -1
- package/dist/utils/fetch.d.ts.map +1 -1
- package/dist/utils/fetch.js +256 -44
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/lspClient.d.ts +6 -0
- package/dist/utils/lspClient.d.ts.map +1 -1
- package/dist/utils/lspClient.js +62 -11
- package/dist/utils/lspClient.js.map +1 -1
- package/dist/utils/websocket.d.ts +6 -0
- package/dist/utils/websocket.d.ts.map +1 -1
- package/dist/utils/websocket.js +15 -7
- package/dist/utils/websocket.js.map +1 -1
- package/package.json +2 -2
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +0 -2
- package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
- package/src/components/dpqlEditor/index.ts +4 -0
- package/src/components/dpqlEditor/types.ts +26 -2
- package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
- package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
- package/src/components/form/engine/CompactRow.tsx +231 -11
- package/src/components/form/engine/CompactToolbar.tsx +10 -3
- package/src/components/form/engine/FormEngine.stories.tsx +331 -30
- package/src/components/form/engine/FormEngine.tsx +504 -87
- package/src/components/form/engine/FormFieldsSkeleton.tsx +76 -0
- package/src/components/form/engine/OptionFieldMessages.tsx +89 -11
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +11 -0
- package/src/components/form/engine/compactToolbarContext.ts +1 -1
- package/src/components/form/engine/readFirst.ts +86 -7
- package/src/components/form/engine/rendererTypes.ts +12 -0
- package/src/components/form/engine/rowMenuContext.ts +159 -0
- package/src/components/form/engine/typeChoices.ts +55 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +828 -31
- package/src/components/form/expressions/ExpressionField.tsx +234 -29
- package/src/components/form/expressions/argumentPresence.ts +200 -0
- package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
- package/src/components/form/expressions/builder/index.tsx +31 -75
- package/src/components/form/expressions/dpqlMockLsp.ts +81 -1
- package/src/components/form/expressions/renderExpressionToText.ts +106 -17
- package/src/components/form/fields/Field.tsx +3 -2
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -2
- package/src/components/form/fields/auto/AutoFormField.tsx +12 -2
- package/src/components/form/fields/rich-text/RichText.tsx +112 -19
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
- package/src/components/form/fields/template/TemplateField.tsx +667 -95
- package/src/components/form/index.tsx +1 -0
- package/src/components/log/Log.stories.tsx +2 -1
- package/src/components/smartEditor/__fixtures__/mockLspServer.ts +6 -1
- package/src/helpers/dpqlDetection.ts +209 -0
- package/src/helpers/options.ts +70 -0
- package/src/helpers/templateItems.ts +81 -0
- package/src/helpers/templates.ts +51 -2
- package/src/helpers/validations.ts +59 -9
- package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
- package/src/hooks/useFetch/useFetch.tsx +53 -7
- package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
- package/src/hooks/useTemplates.ts +41 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
- package/src/index.tsx +14 -1
- package/src/stores/currentUser/currentUser.stories.tsx +2 -1
- package/src/stories/storyNetwork.ts +92 -0
- package/src/utils/fetch.ts +311 -21
- package/src/utils/lspClient.ts +68 -11
- package/src/utils/websocket.ts +20 -9
|
@@ -24,6 +24,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
|
24
24
|
import styled from 'styled-components';
|
|
25
25
|
import { moveItem } from '../../../../helpers/common';
|
|
26
26
|
import { areQorusTypesCompatible, getArgumentType } from '../../../../helpers/expressions';
|
|
27
|
+
import { addMissingExpressionArgs } from '../argumentPresence';
|
|
27
28
|
import { findTemplate } from '../../../../helpers/templates';
|
|
28
29
|
import { validateField, validateFieldWithResult } from '../../../../helpers/validations';
|
|
29
30
|
import { IQorusTypeObject, useQorusTypes } from '../../../../hooks/useQorusTypes';
|
|
@@ -76,6 +77,18 @@ export interface IExpressionBuilderProps {
|
|
|
76
77
|
expressions?: IExpressionSchema[];
|
|
77
78
|
expressionsUrl?: string;
|
|
78
79
|
serverHandled?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* SEAM (reqraft): the host's per-`ui_type` editors, forwarded to the operand
|
|
82
|
+
* fields below.
|
|
83
|
+
*
|
|
84
|
+
* Without them an operand whose type is one of the CONSUMER's own ui_types
|
|
85
|
+
* falls through `AutoFormField`'s switch to the literal "Unknown type!" tag.
|
|
86
|
+
* That is what an assertion's `Value` did the moment it was turned into an
|
|
87
|
+
* expression: the field is `test-reference`, an IDE ui_type whose editor the
|
|
88
|
+
* IDE registers through `componentOverrides`, and the builder rendered its
|
|
89
|
+
* operands without them.
|
|
90
|
+
*/
|
|
91
|
+
componentOverrides?: Record<string, React.FC<any>>;
|
|
79
92
|
size?: string;
|
|
80
93
|
/**
|
|
81
94
|
* SEAM (reqraft): extra hover actions prepended to each expression card —
|
|
@@ -129,6 +142,7 @@ export const Expression = ({
|
|
|
129
142
|
expressionsUrl,
|
|
130
143
|
serverHandled,
|
|
131
144
|
extraActions,
|
|
145
|
+
componentOverrides,
|
|
132
146
|
...props
|
|
133
147
|
}: IExpressionProps) => {
|
|
134
148
|
const types = useQorusTypes();
|
|
@@ -264,58 +278,8 @@ export const Expression = ({
|
|
|
264
278
|
);
|
|
265
279
|
|
|
266
280
|
const addMissingArgs = useCallback(
|
|
267
|
-
(expression: string, args: IExpression[] = []): IExpression[] =>
|
|
268
|
-
|
|
269
|
-
// Check if this expression has variable arguments
|
|
270
|
-
const selectedExpression = expressions.value?.find((exp) => exp.name === expression);
|
|
271
|
-
|
|
272
|
-
if (selectedExpression.varargs) {
|
|
273
|
-
if (selectedExpression.subtype === 2) {
|
|
274
|
-
newArgs.push({
|
|
275
|
-
is_expression: true,
|
|
276
|
-
value: {
|
|
277
|
-
args: [],
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
} else {
|
|
281
|
-
// Add the number of arguments equal to min_args - 1
|
|
282
|
-
newArgs.push(
|
|
283
|
-
...Array.from({ length: selectedExpression.min_args - 1 }, () => ({
|
|
284
|
-
type:
|
|
285
|
-
selectedExpression.args[0].ui_type === 'richtext' ||
|
|
286
|
-
selectedExpression.args[0].ui_type === 'number'
|
|
287
|
-
? selectedExpression.args[0].ui_type
|
|
288
|
-
: undefined,
|
|
289
|
-
}))
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
} else {
|
|
293
|
-
// Check if any of the arguments have a default value
|
|
294
|
-
selectedExpression.args.forEach((arg, index) => {
|
|
295
|
-
if (
|
|
296
|
-
newArgs[index] === undefined ||
|
|
297
|
-
size(newArgs[index]) === 0 ||
|
|
298
|
-
(arg.default_value && 'value' in newArgs[index] === false)
|
|
299
|
-
) {
|
|
300
|
-
if (arg.default_value) {
|
|
301
|
-
newArgs[index] = {
|
|
302
|
-
value: arg.default_value,
|
|
303
|
-
type: arg.ui_type,
|
|
304
|
-
};
|
|
305
|
-
} else {
|
|
306
|
-
newArgs[index] = {
|
|
307
|
-
type:
|
|
308
|
-
arg.ui_type === 'richtext' || arg.ui_type === 'number' || arg.ui_type === 'bool'
|
|
309
|
-
? arg.ui_type
|
|
310
|
-
: undefined,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return newArgs;
|
|
318
|
-
},
|
|
281
|
+
(expression: string, args: IExpression[] = []): IExpression[] =>
|
|
282
|
+
addMissingExpressionArgs(expressions.value as never, expression, args as never) as never,
|
|
319
283
|
[JSON.stringify(expressions.value)]
|
|
320
284
|
);
|
|
321
285
|
|
|
@@ -501,13 +465,7 @@ export const Expression = ({
|
|
|
501
465
|
);
|
|
502
466
|
|
|
503
467
|
const updateArg = useCallback(
|
|
504
|
-
(
|
|
505
|
-
val: any,
|
|
506
|
-
index: number = 0,
|
|
507
|
-
type?: string,
|
|
508
|
-
isFunction?: boolean,
|
|
509
|
-
isRequired?: boolean
|
|
510
|
-
) => {
|
|
468
|
+
(val: any, index: number = 0, type?: string, isFunction?: boolean) => {
|
|
511
469
|
const args = clone(value.value.args);
|
|
512
470
|
const newVal = clone(val);
|
|
513
471
|
|
|
@@ -515,12 +473,18 @@ export const Expression = ({
|
|
|
515
473
|
newVal.args = addMissingArgs(newVal.exp, newVal.args);
|
|
516
474
|
}
|
|
517
475
|
|
|
476
|
+
/* `required` is NOT written here. It is a schema key — it describes the
|
|
477
|
+
catalogue's declaration of the argument, not the author's value, and
|
|
478
|
+
nothing reads it back off a stored argument (validation takes it from
|
|
479
|
+
the schema). Persisting it made a cleared argument indistinguishable
|
|
480
|
+
from real data: the value dropped out as `undefined`, `required: true`
|
|
481
|
+
stayed, and what was saved to the draft was `{type, required}` — an
|
|
482
|
+
argument DEFINITION sitting where its value should be. */
|
|
518
483
|
args[index] = {
|
|
519
484
|
...args[index],
|
|
520
485
|
value: newVal,
|
|
521
486
|
type: !isFunction ? type || args[index]?.type : undefined,
|
|
522
487
|
is_expression: isFunction,
|
|
523
|
-
required: isRequired,
|
|
524
488
|
};
|
|
525
489
|
|
|
526
490
|
onValueChange(
|
|
@@ -683,16 +647,10 @@ export const Expression = ({
|
|
|
683
647
|
const handleFirstParamChange = useCallback(
|
|
684
648
|
(_name, value, type, isFunction) => {
|
|
685
649
|
if (isFunction || (type !== 'any' && type !== 'auto')) {
|
|
686
|
-
updateArg(
|
|
687
|
-
value,
|
|
688
|
-
0,
|
|
689
|
-
isFunction ? undefined : type,
|
|
690
|
-
isFunction,
|
|
691
|
-
selectedExpression?.args?.[0]?.required
|
|
692
|
-
);
|
|
650
|
+
updateArg(value, 0, isFunction ? undefined : type, isFunction);
|
|
693
651
|
}
|
|
694
652
|
},
|
|
695
|
-
[updateArg
|
|
653
|
+
[updateArg]
|
|
696
654
|
);
|
|
697
655
|
|
|
698
656
|
const buildCustomTemplateItems = useCallback(
|
|
@@ -1013,6 +971,7 @@ export const Expression = ({
|
|
|
1013
971
|
>
|
|
1014
972
|
<TemplateField
|
|
1015
973
|
component={auto}
|
|
974
|
+
componentOverrides={componentOverrides}
|
|
1016
975
|
// SEAM (reqraft): an operand that becomes an expression mounts
|
|
1017
976
|
// its own builder through this TemplateField — hand the host's
|
|
1018
977
|
// actions on, or nesting silently drops them (the group
|
|
@@ -1132,6 +1091,7 @@ export const Expression = ({
|
|
|
1132
1091
|
<TemplateField
|
|
1133
1092
|
minimal
|
|
1134
1093
|
component={auto}
|
|
1094
|
+
componentOverrides={componentOverrides}
|
|
1135
1095
|
// SEAM (reqraft): same as the first operand above.
|
|
1136
1096
|
extraActions={extraActions}
|
|
1137
1097
|
noSoft
|
|
@@ -1166,13 +1126,7 @@ export const Expression = ({
|
|
|
1166
1126
|
!!arg.default_value
|
|
1167
1127
|
}
|
|
1168
1128
|
onChange={((_name, value, type, isFunction) => {
|
|
1169
|
-
updateArg(
|
|
1170
|
-
value,
|
|
1171
|
-
index + 1,
|
|
1172
|
-
isFunction ? undefined : type,
|
|
1173
|
-
isFunction,
|
|
1174
|
-
arg.required
|
|
1175
|
-
);
|
|
1129
|
+
updateArg(value, index + 1, isFunction ? undefined : type, isFunction);
|
|
1176
1130
|
}) as any}
|
|
1177
1131
|
fluid={false}
|
|
1178
1132
|
fixed={true}
|
|
@@ -1248,6 +1202,7 @@ export const ExpressionBuilder = ({
|
|
|
1248
1202
|
expressionsUrl,
|
|
1249
1203
|
serverHandled,
|
|
1250
1204
|
extraActions,
|
|
1205
|
+
componentOverrides,
|
|
1251
1206
|
reorder,
|
|
1252
1207
|
}: IExpressionBuilderProps) => {
|
|
1253
1208
|
const templates = useTemplates(!isChild, localTemplates);
|
|
@@ -1432,6 +1387,7 @@ export const ExpressionBuilder = ({
|
|
|
1432
1387
|
id={level === 0 && index === 0 ? 'expression-builder' : undefined}
|
|
1433
1388
|
serverHandled={serverHandled}
|
|
1434
1389
|
extraActions={extraActions}
|
|
1390
|
+
componentOverrides={componentOverrides}
|
|
1435
1391
|
reorder={reorder}
|
|
1436
1392
|
/>
|
|
1437
1393
|
</ReqoreErrorBoundary>
|
|
@@ -19,8 +19,41 @@ interface IMockExprNode {
|
|
|
19
19
|
is_expression?: boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Every `dpql/parse` the mock answered, in order, for diagnosis.
|
|
24
|
+
*
|
|
25
|
+
* The type-analysis stories depend on the front end SENDING `target_type` —
|
|
26
|
+
* the mock returns analysis only when it is present, exactly as the server
|
|
27
|
+
* behaves. When one of those stories fails there are two very different
|
|
28
|
+
* causes, and the rendered DOM cannot tell them apart: the request never
|
|
29
|
+
* carried a target, or it did and the answer was not rendered. This records
|
|
30
|
+
* which, and the story reports it in its assertion message so the answer
|
|
31
|
+
* survives into a CI log.
|
|
32
|
+
*/
|
|
33
|
+
export interface IDpqlMockParseCall {
|
|
34
|
+
text: string;
|
|
35
|
+
target?: string;
|
|
36
|
+
analysed: boolean;
|
|
37
|
+
}
|
|
38
|
+
export const dpqlMockParseCalls: IDpqlMockParseCall[] = [];
|
|
39
|
+
|
|
40
|
+
/** The template namespaces the server offers after a `$`. */
|
|
41
|
+
const DPQL_MOCK_TEMPLATE_ITEMS = [
|
|
42
|
+
{ label: '$data:', insertText: '$data:', kind: 1, detail: 'template' },
|
|
43
|
+
{ label: '$config:', insertText: '$config:', kind: 1, detail: 'template' },
|
|
44
|
+
{ label: '$static:', insertText: '$static:', kind: 1, detail: 'template' },
|
|
45
|
+
{ label: '$timestamp:', insertText: '$timestamp:', kind: 1, detail: 'template' },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/** The record fields the server offers after an `@`. */
|
|
49
|
+
const DPQL_MOCK_FIELD_ITEMS = [
|
|
50
|
+
{ label: '@name', insertText: '@name', kind: 5, detail: 'string' },
|
|
51
|
+
{ label: '@status', insertText: '@status', kind: 5, detail: 'string' },
|
|
52
|
+
];
|
|
53
|
+
|
|
22
54
|
/** Start the mock LSP; returns a teardown function. */
|
|
23
55
|
export const startDpqlMockLsp = (): (() => void) => {
|
|
56
|
+
dpqlMockParseCalls.length = 0;
|
|
24
57
|
const lsp = createMockLspServer(MOCK_LSP_URL, {
|
|
25
58
|
capabilities: {
|
|
26
59
|
textDocumentSync: { openClose: true, change: 2 },
|
|
@@ -29,7 +62,25 @@ export const startDpqlMockLsp = (): (() => void) => {
|
|
|
29
62
|
// Unknown requests get an empty success so nothing hangs.
|
|
30
63
|
defaultResult: {},
|
|
31
64
|
handlers: {
|
|
32
|
-
|
|
65
|
+
/* Position-aware, as the real server is: `$` opens the template
|
|
66
|
+
namespaces, `@` the record fields. Answering the same list whatever
|
|
67
|
+
precedes the cursor would make a story asserting the `$` menu pass
|
|
68
|
+
for a shell that asked in the wrong context — which is the only thing
|
|
69
|
+
such a story is there to catch. (`DpqlEditor`'s own stories carry the
|
|
70
|
+
full catalogue; this is the minimal subset.) */
|
|
71
|
+
'textDocument/completion': (msg, server) => {
|
|
72
|
+
const position = msg.params?.position ?? { line: 0, character: 0 };
|
|
73
|
+
const line = server.documentText.split('\n')[position.line] ?? '';
|
|
74
|
+
const head = line.slice(0, position.character);
|
|
75
|
+
const sigil = head.match(/[@$][\w:.{}]*$/)?.[0]?.[0] ?? '';
|
|
76
|
+
if (sigil === '$') {
|
|
77
|
+
return { isIncomplete: false, items: DPQL_MOCK_TEMPLATE_ITEMS };
|
|
78
|
+
}
|
|
79
|
+
if (sigil === '@') {
|
|
80
|
+
return { isIncomplete: false, items: DPQL_MOCK_FIELD_ITEMS };
|
|
81
|
+
}
|
|
82
|
+
return { isIncomplete: false, items: [] };
|
|
83
|
+
},
|
|
33
84
|
|
|
34
85
|
'textDocument/semanticTokens/full': () => ({ data: [] }),
|
|
35
86
|
|
|
@@ -39,6 +90,34 @@ export const startDpqlMockLsp = (): (() => void) => {
|
|
|
39
90
|
// deterministic and verifiable.
|
|
40
91
|
'dpql/parse': (msg) => {
|
|
41
92
|
const text = (msg.params?.text ?? '').trim();
|
|
93
|
+
// Type analysis is returned ONLY when the request carried a
|
|
94
|
+
// `target_type`, exactly as the server behaves. That makes it a real
|
|
95
|
+
// check rather than a fixture: a front end that stops sending the
|
|
96
|
+
// target gets no analysis, and the stories asserting the message go
|
|
97
|
+
// red instead of quietly passing.
|
|
98
|
+
const target = msg.params?.target_type as string | undefined;
|
|
99
|
+
const inferred = 'string';
|
|
100
|
+
const analysis =
|
|
101
|
+
target ?
|
|
102
|
+
{
|
|
103
|
+
inferred_type: inferred,
|
|
104
|
+
target_type: target,
|
|
105
|
+
type_compatible: target === inferred || target === 'auto',
|
|
106
|
+
auto_coercible: true,
|
|
107
|
+
// "text used as a number" is the attempted-but-not-guaranteed
|
|
108
|
+
// conversion; "text used as text" needs no conversion at all.
|
|
109
|
+
coercion_may_fail: target === 'int' || target === 'float',
|
|
110
|
+
suggested_fix:
|
|
111
|
+
target === inferred || target === 'auto' ?
|
|
112
|
+
undefined
|
|
113
|
+
: { text: `to${target[0].toUpperCase()}${target.slice(1)}(${text})` },
|
|
114
|
+
}
|
|
115
|
+
: {};
|
|
116
|
+
dpqlMockParseCalls.push({
|
|
117
|
+
text,
|
|
118
|
+
target,
|
|
119
|
+
analysed: !!target,
|
|
120
|
+
});
|
|
42
121
|
return {
|
|
43
122
|
success: true,
|
|
44
123
|
expression: {
|
|
@@ -51,6 +130,7 @@ export const startDpqlMockLsp = (): (() => void) => {
|
|
|
51
130
|
],
|
|
52
131
|
},
|
|
53
132
|
},
|
|
133
|
+
...analysis,
|
|
54
134
|
diagnostics: [],
|
|
55
135
|
};
|
|
56
136
|
},
|
|
@@ -10,6 +10,67 @@ import { IExpression, IExpressionSchema, IExpressionValue } from './types';
|
|
|
10
10
|
const isOperatorSymbol = (symbol?: string): boolean =>
|
|
11
11
|
!!symbol && /^[^\w\s]+$/.test(symbol);
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Operator precedence, lower binds tighter.
|
|
15
|
+
*
|
|
16
|
+
* Mirrors `DpqlOperatorPrecedence` in the Qore DataProvider module, which is
|
|
17
|
+
* the source of truth — the server renders the same ASTs through
|
|
18
|
+
* `DataProvider::renderExpression`, and an offline fallback that parenthesised
|
|
19
|
+
* differently would make the same expression read two ways depending on
|
|
20
|
+
* whether the instance happened to be reachable.
|
|
21
|
+
*/
|
|
22
|
+
const PRECEDENCE: Record<string, number> = {
|
|
23
|
+
'*': 1,
|
|
24
|
+
'/': 1,
|
|
25
|
+
'%': 1,
|
|
26
|
+
'+': 2,
|
|
27
|
+
'-': 2,
|
|
28
|
+
'!': 3,
|
|
29
|
+
'==': 4,
|
|
30
|
+
'!=': 4,
|
|
31
|
+
'<': 4,
|
|
32
|
+
'<=': 4,
|
|
33
|
+
'>': 4,
|
|
34
|
+
'>=': 4,
|
|
35
|
+
'&&': 5,
|
|
36
|
+
'||': 6,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Anything that is not an operator; nothing binds tighter. */
|
|
40
|
+
const PREC_PRIMARY = 0;
|
|
41
|
+
/** Looser than every operator, so a value rendered in it is never wrapped. */
|
|
42
|
+
const PREC_LOOSEST = 7;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Operators whose grouping cannot change the result.
|
|
46
|
+
*
|
|
47
|
+
* `+` is deliberately absent: it concatenates when an operand is a string, so
|
|
48
|
+
* `1 + (2 + "a")` is `"12a"` where `1 + 2 + "a"` is `"3a"`. Dropping its
|
|
49
|
+
* parentheses would not be a cosmetic simplification.
|
|
50
|
+
*/
|
|
51
|
+
const ASSOCIATIVE = new Set(['&&', '||']);
|
|
52
|
+
|
|
53
|
+
const precedenceOf = (exp?: string): number => PRECEDENCE[exp ?? ''] ?? PREC_PRIMARY;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The context an operator's argument is rendered in — the same rule as
|
|
57
|
+
* `DpqlSerializer::getArgumentContext()`.
|
|
58
|
+
*
|
|
59
|
+
* The first argument is where left-associative parsing puts a same-precedence
|
|
60
|
+
* child back, so it needs no parentheses; a later one does, or `1 - (2 - 3)`
|
|
61
|
+
* reads as `1 - 2 - 3`, which is a different expression.
|
|
62
|
+
*/
|
|
63
|
+
const argumentContext = (exp: string, argn: number): number => {
|
|
64
|
+
const prec = precedenceOf(exp);
|
|
65
|
+
return !argn || ASSOCIATIVE.has(exp) ? prec : prec - 1;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** An argument reaches this as a bare value or as a `{type, value}` envelope. */
|
|
69
|
+
const readArgValue = (arg: unknown): unknown =>
|
|
70
|
+
arg !== null && typeof arg === 'object' && 'value' in (arg as object)
|
|
71
|
+
? (arg as { value: unknown }).value
|
|
72
|
+
: arg;
|
|
73
|
+
|
|
13
74
|
const renderLiteral = (value: unknown): string => {
|
|
14
75
|
if (value === undefined || value === null) return 'null';
|
|
15
76
|
if (typeof value === 'string') return JSON.stringify(value);
|
|
@@ -17,31 +78,59 @@ const renderLiteral = (value: unknown): string => {
|
|
|
17
78
|
return String(value);
|
|
18
79
|
};
|
|
19
80
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
* approximation, used as the fallback when the LSP is unreachable.
|
|
23
|
-
*/
|
|
24
|
-
export const renderExpressionToText = (
|
|
81
|
+
/** Renders `value` and wraps it only if it binds looser than `contextPrec`. */
|
|
82
|
+
const renderInContext = (
|
|
25
83
|
value: IExpressionValue | undefined,
|
|
26
|
-
expressions: IExpressionSchema[]
|
|
84
|
+
expressions: IExpressionSchema[],
|
|
85
|
+
contextPrec: number
|
|
27
86
|
): string => {
|
|
28
87
|
if (!value?.exp) return '';
|
|
88
|
+
/* A `value` expression IS its literal.
|
|
89
|
+
|
|
90
|
+
It is the wrapper the parser puts around a bare literal — typing `null`,
|
|
91
|
+
or `42`, into the text editor produces one — and it carries no symbol, so
|
|
92
|
+
rendering it through the generic paths printed the wrapper instead of the
|
|
93
|
+
value: `value(42)` with a catalogue that names it, `(42)` with one that
|
|
94
|
+
supplies its empty symbol. The server renders the literal alone, and a
|
|
95
|
+
summary that disagrees with the preview beside it is worse than either. */
|
|
96
|
+
if (value.exp === 'value' && (value.args ?? []).length === 1) {
|
|
97
|
+
const only = (value.args ?? [])[0] as IExpression;
|
|
98
|
+
if (!only?.is_expression) {
|
|
99
|
+
return renderLiteral(readArgValue(only));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
29
102
|
const schema = expressions.find((e) => e.name === value.exp);
|
|
30
103
|
const symbol = schema?.symbol ?? value.exp;
|
|
31
|
-
const args =
|
|
104
|
+
const args = value.args ?? [];
|
|
105
|
+
// Logical group (subtype 2) → join with the symbol.
|
|
106
|
+
const isLogicalGroup = schema?.subtype === 2 || value.exp === '&&' || value.exp === '||';
|
|
107
|
+
// Binary operator → infix. Everything else renders as a call, whose own
|
|
108
|
+
// parentheses and commas already delimit its arguments.
|
|
109
|
+
const isInfix = isLogicalGroup || (args.length === 2 && isOperatorSymbol(symbol));
|
|
110
|
+
|
|
111
|
+
const parts = args.map((arg: IExpression, argn: number) =>
|
|
32
112
|
arg?.is_expression && arg.value?.exp
|
|
33
|
-
?
|
|
113
|
+
? renderInContext(
|
|
114
|
+
arg.value,
|
|
115
|
+
expressions,
|
|
116
|
+
isInfix ? argumentContext(value.exp as string, argn) : PREC_LOOSEST
|
|
117
|
+
)
|
|
34
118
|
: renderLiteral(arg?.value)
|
|
35
119
|
);
|
|
36
120
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return args.join(` ${symbol} `);
|
|
121
|
+
if (!isInfix) {
|
|
122
|
+
return `${symbol}(${parts.join(', ')})`;
|
|
40
123
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
// Otherwise function form.
|
|
46
|
-
return `${symbol}(${args.join(', ')})`;
|
|
124
|
+
|
|
125
|
+
const text = isLogicalGroup ? parts.join(` ${symbol} `) : `${parts[0]} ${symbol} ${parts[1]}`;
|
|
126
|
+
return precedenceOf(value.exp) > contextPrec ? `(${text})` : text;
|
|
47
127
|
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Recursively render an expression AST to readable text — the client-side
|
|
131
|
+
* approximation, used as the fallback when the LSP is unreachable.
|
|
132
|
+
*/
|
|
133
|
+
export const renderExpressionToText = (
|
|
134
|
+
value: IExpressionValue | undefined,
|
|
135
|
+
expressions: IExpressionSchema[] = []
|
|
136
|
+
): string => renderInContext(value, expressions, PREC_LOOSEST);
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
IReqoreLabelProps,
|
|
3
3
|
ReqoreCheckbox,
|
|
4
4
|
ReqoreControlGroup,
|
|
5
|
-
ReqoreSkeleton,
|
|
6
5
|
ReqoreSpan,
|
|
7
6
|
} from '@qoretechnologies/reqore';
|
|
8
7
|
import { IWithReqoreSize } from '@qoretechnologies/reqore/dist/types/global';
|
|
@@ -38,6 +37,7 @@ import { IDataSchemaDefinition } from './schema-definition/types';
|
|
|
38
37
|
// Direct import — circular dep (FormEngine → TemplateField → FormField → FormEngine) is safe
|
|
39
38
|
// because modules are all loaded before any component renders
|
|
40
39
|
import { FormEngine } from '../engine/FormEngine';
|
|
40
|
+
import { FormFieldsSkeleton } from '../engine/FormFieldsSkeleton';
|
|
41
41
|
|
|
42
42
|
/** Soft Qore types (softint, softstring, …) render as their concrete base —
|
|
43
43
|
* the soft/hard distinction is value-level, not UI. */
|
|
@@ -345,7 +345,8 @@ export const FormField = <T extends TFormFieldType>({
|
|
|
345
345
|
|
|
346
346
|
case 'hash': {
|
|
347
347
|
if (argSchemaLoading) {
|
|
348
|
-
|
|
348
|
+
// A hash renders a nested FORM, so it waits in the shape of one.
|
|
349
|
+
return <FormFieldsSkeleton rows={2} />;
|
|
349
350
|
}
|
|
350
351
|
if (finalArgSchema) {
|
|
351
352
|
return (
|
|
@@ -40,13 +40,23 @@ const DISALLOW_COMPACT_AO_TYPES = ['connection'];
|
|
|
40
40
|
* Whether a creatable field's value is carried by the chip picker rather than
|
|
41
41
|
* by a raw editor with a suggestion list beside it.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
43
|
+
* String-valued only, and only with something to suggest. Every other shape a
|
|
44
44
|
* creatable field can declare either does not fit a one-line chip (a document
|
|
45
45
|
* — `long-string`, `binary`, `data`; a hash; a list) or would come back out of
|
|
46
46
|
* one as a string (a number, a date), and a field with nothing to offer is
|
|
47
47
|
* better served by the editor it has always had. Those keep today's rendering:
|
|
48
48
|
* the raw editor, with the saved-and-suggested picker under it.
|
|
49
49
|
*
|
|
50
|
+
* `select-string` counts, and leaving it out was the bug. A picker field
|
|
51
|
+
* declares its storage type (`*string`) and its `ui_type` (`select-string`),
|
|
52
|
+
* and by the time a schema reaches here the two have been flattened to one:
|
|
53
|
+
* `type` arrives as `select-string`. So this returned false for every such
|
|
54
|
+
* field and NEITHER renderer stood down — the picker drew the value, and the
|
|
55
|
+
* saved-and-suggested list drew the same candidates again underneath it. A
|
|
56
|
+
* test's Service and Method fields showed 183 and 7 items twice over, in two
|
|
57
|
+
* controls that set one value. A select-string's value IS a string and fits the
|
|
58
|
+
* chip, which is the only thing this predicate is really asking.
|
|
59
|
+
*
|
|
50
60
|
* Both the renderer of the picker (`FieldAllowedValues`) and the renderer of
|
|
51
61
|
* the raw editor (`AutoFormField`, `FormField`) ask this, so that exactly one
|
|
52
62
|
* of them draws the value.
|
|
@@ -55,7 +65,8 @@ export const rendersCreatableValueSelect = (
|
|
|
55
65
|
type?: string,
|
|
56
66
|
allowCreation?: boolean,
|
|
57
67
|
items?: unknown[]
|
|
58
|
-
): boolean =>
|
|
68
|
+
): boolean =>
|
|
69
|
+
!!allowCreation && (type === 'string' || type === 'select-string') && count(items) > 0;
|
|
59
70
|
|
|
60
71
|
export const FieldAllowedValuesCheckGroup = memo(
|
|
61
72
|
({
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
ReqoreControlGroup,
|
|
8
8
|
ReqoreErrorBoundary,
|
|
9
9
|
ReqoreMessage,
|
|
10
|
-
ReqoreSpinner,
|
|
11
10
|
ReqoreTag,
|
|
12
11
|
} from '@qoretechnologies/reqore';
|
|
13
12
|
import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
|
|
@@ -53,6 +52,7 @@ import { IDataSchemaDefinition } from '../schema-definition/types';
|
|
|
53
52
|
import { ISelectFormFieldItem, SelectFormField } from '../select/Select';
|
|
54
53
|
import { StringFormField } from '../string/String';
|
|
55
54
|
import { UrlFormField } from '../url/Url';
|
|
55
|
+
import { FormFieldsSkeleton } from '../../engine/FormFieldsSkeleton';
|
|
56
56
|
|
|
57
57
|
/** UI superset of `TQorusType` — the IDE's `Field/systemOptions` `IQorusType`. */
|
|
58
58
|
export type IQorusType = TQorusType | string;
|
|
@@ -395,6 +395,13 @@ function AutoField<T = any>({
|
|
|
395
395
|
arg_schema={arg_schema}
|
|
396
396
|
element_type={element_type}
|
|
397
397
|
ui_element_type={ui_element_type}
|
|
398
|
+
// Same reason as the three above: it is destructured out of `rest` so the
|
|
399
|
+
// primitive renderers do not spread it onto a DOM node, which also took
|
|
400
|
+
// it away from host editors — and a host editor is exactly what
|
|
401
|
+
// `inherit_props` exists to feed. A field declaring it got the values
|
|
402
|
+
// resolved and then had nowhere to send them, so every consumer
|
|
403
|
+
// downstream saw `undefined` and silently did nothing.
|
|
404
|
+
inheritedFromParent={inheritedFromParent}
|
|
398
405
|
name={name}
|
|
399
406
|
value={value}
|
|
400
407
|
onChange={(val: any, emittedType?: IQorusType, emittedIsFunction?: boolean) => {
|
|
@@ -457,7 +464,10 @@ function AutoField<T = any>({
|
|
|
457
464
|
}
|
|
458
465
|
|
|
459
466
|
if (arg_schema && !finalArgSchema) {
|
|
460
|
-
|
|
467
|
+
/* One ROW, because this stands in for one field. It was a spinner reading
|
|
468
|
+
"Loading field data…", which is a different picture from every other
|
|
469
|
+
wait on the page and announces a delay the reader cannot act on. */
|
|
470
|
+
return <FormFieldsSkeleton rows={1} />;
|
|
461
471
|
}
|
|
462
472
|
|
|
463
473
|
const renderAllowedValues = (currentType: IQorusType) => {
|