@qoretechnologies/reqraft 0.10.54 → 0.10.55-pr.115.g37abdfc
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_COMPACT.md +34 -0
- package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
- package/design/FORM_ENGINE_FIELD_MIGRATION.md +155 -1
- package/design/IDE_INTEGRATION.md +13 -0
- package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
- package/design/SELECT_DIALOG_FLOOR.md +86 -0
- package/design/SMART_EDITOR_UX.md +21 -0
- package/design/UNAVAILABLE_ALLOWED_VALUES.md +226 -0
- package/dist/components/composer/Composer.d.ts +8 -3
- package/dist/components/composer/Composer.d.ts.map +1 -1
- package/dist/components/composer/Composer.js.map +1 -1
- package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
- package/dist/components/dpqlEditor/DpqlEditor.js +3 -3
- package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts +6 -0
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.js +21 -2
- package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
- 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/dpqlTags.d.ts +8 -0
- package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlTags.js +5 -1
- package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
- 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 +42 -2
- package/dist/components/dpqlEditor/types.d.ts.map +1 -1
- package/dist/components/dpqlEditor/types.js +0 -4
- package/dist/components/dpqlEditor/types.js.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 +414 -128
- 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 +272 -19
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +666 -161
- 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 +41 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
- package/dist/components/form/engine/OptionFieldMessages.d.ts +108 -2
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +203 -13
- 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 +68 -5
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +15 -1
- package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowContext.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +55 -29
- package/dist/components/form/engine/compactRowStyles.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/optionActions.d.ts +37 -1
- package/dist/components/form/engine/optionActions.d.ts.map +1 -1
- package/dist/components/form/engine/optionActions.js +71 -1
- package/dist/components/form/engine/optionActions.js.map +1 -1
- package/dist/components/form/engine/optionsContext.d.ts +22 -0
- package/dist/components/form/engine/optionsContext.d.ts.map +1 -0
- package/dist/components/form/engine/optionsContext.js +20 -0
- package/dist/components/form/engine/optionsContext.js.map +1 -0
- 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 +76 -8
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +42 -3
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
- package/dist/components/form/engine/rendererTypes.js +3 -1
- package/dist/components/form/engine/rendererTypes.js.map +1 -1
- package/dist/components/form/engine/rowFocus.d.ts +30 -0
- package/dist/components/form/engine/rowFocus.d.ts.map +1 -0
- package/dist/components/form/engine/rowFocus.js +93 -0
- package/dist/components/form/engine/rowFocus.js.map +1 -0
- 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/rowOpenPicker.d.ts +27 -0
- package/dist/components/form/engine/rowOpenPicker.d.ts.map +1 -0
- package/dist/components/form/engine/rowOpenPicker.js +30 -0
- package/dist/components/form/engine/rowOpenPicker.js.map +1 -0
- package/dist/components/form/engine/typeChoices.d.ts +42 -0
- package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
- package/dist/components/form/engine/typeChoices.js +33 -0
- package/dist/components/form/engine/typeChoices.js.map +1 -0
- package/dist/components/form/expressions/DpqlRendering.d.ts +26 -0
- package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -0
- package/dist/components/form/expressions/DpqlRendering.js +53 -0
- package/dist/components/form/expressions/DpqlRendering.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 +203 -45
- package/dist/components/form/expressions/ExpressionField.js.map +1 -1
- package/dist/components/form/expressions/argumentPresence.d.ts +84 -0
- package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
- package/dist/components/form/expressions/argumentPresence.js +217 -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 +21 -65
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.d.ts +4 -1
- package/dist/components/form/expressions/builder/renderTemplate.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.js +10 -67
- package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
- package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/mockExpressions.js +8 -4
- package/dist/components/form/expressions/mockExpressions.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +95 -19
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/expressions/types.d.ts +7 -0
- package/dist/components/form/expressions/types.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +5 -2
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.d.ts +7 -16
- package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.js +50 -126
- package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts +18 -0
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +54 -8
- 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 +52 -8
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +7 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +91 -28
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/date/Date.d.ts +1 -1
- package/dist/components/form/fields/date/Date.d.ts.map +1 -1
- package/dist/components/form/fields/date/Date.js +16 -2
- package/dist/components/form/fields/date/Date.js.map +1 -1
- package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
- package/dist/components/form/fields/long-string/LongString.js +14 -2
- package/dist/components/form/fields/long-string/LongString.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +50 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts +47 -2
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +153 -12
- package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +120 -20
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +166 -9
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +200 -7
- package/dist/components/form/fields/select/SelectCollection.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 +34 -2
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +606 -58
- package/dist/components/form/fields/template/TemplateField.js.map +1 -1
- package/dist/components/form/fields/template/TemplateText.d.ts +36 -0
- package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -0
- package/dist/components/form/fields/template/TemplateText.js +80 -0
- package/dist/components/form/fields/template/TemplateText.js.map +1 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +4 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
- package/dist/components/smartEditor/SmartEditor.js +29 -8
- package/dist/components/smartEditor/SmartEditor.js.map +1 -1
- package/dist/components/smartEditor/useLspSession.d.ts +5 -1
- package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspSession.js +16 -2
- package/dist/components/smartEditor/useLspSession.js.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.d.ts +8 -4
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +7 -3
- package/dist/helpers/common.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 +31 -3
- package/dist/helpers/optionUiTypes.d.ts.map +1 -1
- package/dist/helpers/optionUiTypes.js +36 -1
- package/dist/helpers/optionUiTypes.js.map +1 -1
- package/dist/helpers/options.d.ts +57 -0
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +97 -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/templateText.d.ts +80 -0
- package/dist/helpers/templateText.d.ts.map +1 -0
- package/dist/helpers/templateText.js +291 -0
- package/dist/helpers/templateText.js.map +1 -0
- package/dist/helpers/templates.d.ts +30 -12
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +91 -33
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts +17 -8
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +117 -22
- 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/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 +8 -4
- package/src/components/composer/Composer.tsx +7 -2
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +3 -119
- package/src/components/dpqlEditor/DpqlEditor.tsx +5 -2
- package/src/components/dpqlEditor/dpqlHelpers.ts +20 -1
- package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
- package/src/components/dpqlEditor/dpqlTags.ts +13 -0
- package/src/components/dpqlEditor/index.ts +4 -0
- package/src/components/dpqlEditor/types.ts +38 -2
- package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
- package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
- package/src/components/form/engine/CompactRow.tsx +487 -160
- package/src/components/form/engine/CompactToolbar.tsx +10 -3
- package/src/components/form/engine/FormEngine.stories.tsx +1294 -161
- package/src/components/form/engine/FormEngine.tsx +916 -213
- package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +331 -0
- package/src/components/form/engine/FormFieldsSkeleton.tsx +77 -0
- package/src/components/form/engine/OptionFieldMessages.tsx +300 -14
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +94 -3
- package/src/components/form/engine/compactRowContext.ts +15 -6
- package/src/components/form/engine/compactRowStyles.ts +56 -2
- package/src/components/form/engine/compactToolbarContext.ts +1 -1
- package/src/components/form/engine/optionActions.ts +89 -1
- package/src/components/form/engine/optionsContext.ts +24 -0
- package/src/components/form/engine/readFirst.ts +88 -8
- package/src/components/form/engine/rendererTypes.ts +48 -4
- package/src/components/form/engine/rowFocus.ts +101 -0
- package/src/components/form/engine/rowMenuContext.ts +159 -0
- package/src/components/form/engine/rowOpenPicker.ts +27 -0
- package/src/components/form/engine/typeChoices.ts +63 -0
- package/src/components/form/expressions/DpqlRendering.tsx +85 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
- package/src/components/form/expressions/ExpressionField.tsx +269 -55
- package/src/components/form/expressions/argumentPresence.ts +213 -0
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +38 -21
- package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
- package/src/components/form/expressions/builder/index.tsx +39 -81
- package/src/components/form/expressions/builder/renderTemplate.tsx +23 -56
- package/src/components/form/expressions/dpqlMockLanguage.ts +565 -0
- package/src/components/form/expressions/dpqlMockLsp.ts +109 -83
- package/src/components/form/expressions/mockExpressions.ts +8 -4
- package/src/components/form/expressions/renderExpressionToText.ts +112 -22
- package/src/components/form/expressions/types.ts +7 -0
- package/src/components/form/expressions/useExpressions.ts +5 -1
- package/src/components/form/expressions/useRenderExpression.ts +43 -127
- package/src/components/form/fields/Field.tsx +99 -20
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +106 -44
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +85 -18
- package/src/components/form/fields/auto/AutoFormField.tsx +97 -21
- package/src/components/form/fields/date/Date.tsx +17 -1
- package/src/components/form/fields/long-string/LongString.tsx +15 -3
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +65 -6
- package/src/components/form/fields/rich-text/RichText.tsx +248 -30
- package/src/components/form/fields/select/Select.stories.tsx +392 -0
- package/src/components/form/fields/select/Select.tsx +164 -27
- package/src/components/form/fields/select/SelectCollection.tsx +350 -6
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
- package/src/components/form/fields/template/TemplateField.stories.tsx +93 -14
- package/src/components/form/fields/template/TemplateField.tsx +825 -131
- package/src/components/form/fields/template/TemplateText.tsx +115 -0
- package/src/components/form/index.tsx +4 -0
- package/src/components/log/Log.stories.tsx +2 -1
- package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +3 -3
- package/src/components/smartEditor/SmartEditor.tsx +41 -9
- package/src/components/smartEditor/__fixtures__/mockLspServer.ts +52 -3
- package/src/components/smartEditor/useLspSession.ts +21 -3
- package/src/components/supportTicket/TicketReplyBox.tsx +8 -4
- package/src/components/ticketThread/TicketThread.stories.tsx +1 -0
- package/src/helpers/common.ts +7 -3
- package/src/helpers/dpqlDetection.ts +209 -0
- package/src/helpers/optionUiTypes.ts +38 -0
- package/src/helpers/options.ts +96 -0
- package/src/helpers/templateItems.ts +81 -0
- package/src/helpers/templateText.ts +350 -0
- package/src/helpers/templates.ts +94 -41
- package/src/helpers/validations.ts +138 -39
- 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
- package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts +0 -73
- package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts.map +0 -1
- package/dist/components/form/engine/__fixtures__/basicFormOptions.js +0 -317
- package/dist/components/form/engine/__fixtures__/basicFormOptions.js.map +0 -1
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts +0 -41
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts.map +0 -1
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js +0 -45
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js.map +0 -1
- package/dist/components/form/expressions/dpqlMockLsp.d.ts +0 -3
- package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +0 -1
- package/dist/components/form/expressions/dpqlMockLsp.js +0 -91
- package/dist/components/form/expressions/dpqlMockLsp.js.map +0 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +0 -2
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +0 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +0 -9
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +0 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +0 -55
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +0 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js +0 -126
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +0 -1
- package/dist/stories/Data/fileFieldArgSchema.json +0 -86
package/src/helpers/templates.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { modalStore } from '@qoretechnologies/reqore';
|
|
2
|
+
import { TReqoreTooltipProp } from '@qoretechnologies/reqore/dist/types/global';
|
|
2
3
|
import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
|
|
3
4
|
import {
|
|
4
5
|
TReqoreDropdownItem,
|
|
@@ -20,6 +21,24 @@ import { areQorusTypesCompatible } from './expressions';
|
|
|
20
21
|
*/
|
|
21
22
|
export const TEMPLATE_EXAMPLE_PREVIEW_LENGTH = 150;
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* How template picker entries are ordered: by the label a reader actually sees.
|
|
26
|
+
*
|
|
27
|
+
* The server sends groups and their items in whatever order it assembled them —
|
|
28
|
+
* for the test vocabulary that is step order, for an app's actions it is the
|
|
29
|
+
* connector's own. Neither is an order a reader can predict, and a picker
|
|
30
|
+
* holding 29 items is navigated by looking for a NAME.
|
|
31
|
+
*
|
|
32
|
+
* `localeCompare` with `numeric` so `item2` sorts before `item10`, and
|
|
33
|
+
* `sensitivity: 'base'` so case and accents do not split otherwise-adjacent
|
|
34
|
+
* neighbours.
|
|
35
|
+
*/
|
|
36
|
+
const byLabel = (a: { label?: unknown }, b: { label?: unknown }): number =>
|
|
37
|
+
String(a?.label ?? '').localeCompare(String(b?.label ?? ''), undefined, {
|
|
38
|
+
numeric: true,
|
|
39
|
+
sensitivity: 'base',
|
|
40
|
+
});
|
|
41
|
+
|
|
23
42
|
/**
|
|
24
43
|
* Shared template string utilities.
|
|
25
44
|
* Used by both validations and TemplateField.
|
|
@@ -106,9 +125,42 @@ const matchesTemplateValue = (item: TReqoreDropdownItem, value: string): boolean
|
|
|
106
125
|
/** Splits `$data:{a.b.c}` into its key (`$data`) and inner path (`a.b.c`). */
|
|
107
126
|
const TEMPLATE_TOKEN_PATH = /^(\$[A-Za-z_][\w-]*):\{(.*)\}$/;
|
|
108
127
|
|
|
128
|
+
/**
|
|
129
|
+
* The KEY-LESS spelling — `$.create.status`, `$._case.mode`, `$.order[0].sku`.
|
|
130
|
+
*
|
|
131
|
+
* A host may name its own references without a key and without braces: a Qorus
|
|
132
|
+
* test reads what a step captured as `$.create`, and that is the whole token.
|
|
133
|
+
* The path is captured WITH its leading `.`, so a prefix comparison is between
|
|
134
|
+
* two strings of the same shape and `$.create` still ends at a path boundary
|
|
135
|
+
* inside `$.create.status`.
|
|
136
|
+
*
|
|
137
|
+
* Deliberately strict — a root segment, then any number of further `.field`
|
|
138
|
+
* steps and `[0]` indices, and nothing else — so it cannot claim a keyed token
|
|
139
|
+
* (which carries a `:`) or a bare `$word`.
|
|
140
|
+
*/
|
|
141
|
+
const KEYLESS_TOKEN_PATH = /^\$((?:\.[A-Za-z_][\w-]*|\[\d+\])+)$/;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* "No key" is a key of its own.
|
|
145
|
+
*
|
|
146
|
+
* `findTemplateByPath` refuses to name a value from an item under a different
|
|
147
|
+
* key, and the key-less grammar has to take part in that rule rather than sit
|
|
148
|
+
* outside it: a `$data:{create.status}` item must not name `$.create.status`,
|
|
149
|
+
* and a `$.create` item must not name `$data:{create.status}`. Every keyed
|
|
150
|
+
* token's key begins with `$`, so the empty string can never collide with one.
|
|
151
|
+
*/
|
|
152
|
+
const NO_TOKEN_KEY = '';
|
|
153
|
+
|
|
109
154
|
const getTokenPath = (value?: unknown): { key: string; path: string } | undefined => {
|
|
110
|
-
|
|
111
|
-
|
|
155
|
+
if (typeof value !== 'string') {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
const keyed = TEMPLATE_TOKEN_PATH.exec(value);
|
|
159
|
+
if (keyed) {
|
|
160
|
+
return { key: keyed[1], path: keyed[2] };
|
|
161
|
+
}
|
|
162
|
+
const keyless = KEYLESS_TOKEN_PATH.exec(value);
|
|
163
|
+
return keyless ? { key: NO_TOKEN_KEY, path: keyless[1] } : undefined;
|
|
112
164
|
};
|
|
113
165
|
|
|
114
166
|
/**
|
|
@@ -124,7 +176,12 @@ const getTokenPath = (value?: unknown): { key: string; path: string } | undefine
|
|
|
124
176
|
*
|
|
125
177
|
* The match must break at a path boundary (`.` or `[`) so `choicesOther` is
|
|
126
178
|
* never named after `choices`, and the token keys must agree so a `$config:`
|
|
127
|
-
* item never names a `$data:` value
|
|
179
|
+
* item never names a `$data:` value — "no key" included, so the key-less
|
|
180
|
+
* grammar and the keyed one never name each other's references.
|
|
181
|
+
*
|
|
182
|
+
* Both grammars arrive here: `$data:{create.choices[0]}` and the key-less
|
|
183
|
+
* `$.create.status` are the same question asked in two spellings, and a
|
|
184
|
+
* catalogue written in either one names the walks its author took past it.
|
|
128
185
|
*/
|
|
129
186
|
export const findTemplateByPath = (
|
|
130
187
|
templates: IReqoreFormTemplates,
|
|
@@ -239,9 +296,14 @@ export const resolveTemplateLabel = (
|
|
|
239
296
|
* never fetches one), and there the raw token is the only thing left to show.
|
|
240
297
|
* Showing it as `$data:{…}` renders a value as code; showing the path renders
|
|
241
298
|
* it as a name, which is the honest floor.
|
|
299
|
+
*
|
|
300
|
+
* Only a BRACED wrapper is stripped, because only a braced wrapper is one.
|
|
301
|
+
* A key-less `$._case.mode` stripped of its `$` reads as `._case.mode`, which
|
|
302
|
+
* is neither the reference nor a name for it; with nothing to call it, the
|
|
303
|
+
* reference as written is the honest answer.
|
|
242
304
|
*/
|
|
243
305
|
export const getTemplateReferencePath = (value: string): string =>
|
|
244
|
-
|
|
306
|
+
(typeof value === 'string' ? TEMPLATE_TOKEN_PATH.exec(value)?.[2] : undefined) ?? value;
|
|
245
307
|
|
|
246
308
|
/**
|
|
247
309
|
* The best available name for a reference, for a surface that must show
|
|
@@ -258,43 +320,34 @@ export const describeTemplateReference = (
|
|
|
258
320
|
return resolved.item ? resolved : { label: getTemplateReferencePath(value) };
|
|
259
321
|
};
|
|
260
322
|
|
|
261
|
-
const EMBEDDED_TEMPLATE_TOKEN = new RegExp(TEMPLATE_TOKEN_SOURCE, 'g');
|
|
262
|
-
|
|
263
|
-
export type TTemplateTextSegment =
|
|
264
|
-
| { kind: 'text'; text: string }
|
|
265
|
-
| { kind: 'token'; text: string };
|
|
266
|
-
|
|
267
323
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
324
|
+
* What a chosen template's hover should say.
|
|
325
|
+
*
|
|
326
|
+
* The reference itself — `$._case.title`, `$data:{dc_ai_reply.choices[0]…}` —
|
|
327
|
+
* is an implementation detail of the grammar, and it was what every template
|
|
328
|
+
* chip put in its tooltip. An author hovering a chip wants to know WHAT VALUE
|
|
329
|
+
* this is, and the catalogue already carries that: the entry's `description`
|
|
330
|
+
* is the prose the picker lists it under, written from the schema that declares
|
|
331
|
+
* it, with its type appended.
|
|
332
|
+
*
|
|
333
|
+
* So the hover reads as a small card — the value's name as the title, its
|
|
334
|
+
* description as the body — and the path stays where it is genuinely needed:
|
|
335
|
+
* the field's own help, which teaches the grammar with a worked example, and
|
|
336
|
+
* the picker, for the walks no list can hold.
|
|
337
|
+
*
|
|
338
|
+
* Falls back to the resolved label (itself the stripped path when the catalogue
|
|
339
|
+
* explains nothing), because a surface with no catalogue must still say what
|
|
340
|
+
* the value is rather than nothing at all.
|
|
271
341
|
*/
|
|
272
|
-
export const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
// calls and would skip tokens on the second string it is given.
|
|
282
|
-
const pattern = new RegExp(EMBEDDED_TEMPLATE_TOKEN.source, 'g');
|
|
283
|
-
let match: RegExpExecArray | null;
|
|
284
|
-
|
|
285
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
286
|
-
if (match.index > lastIndex) {
|
|
287
|
-
segments.push({ kind: 'text', text: text.slice(lastIndex, match.index) });
|
|
288
|
-
}
|
|
289
|
-
segments.push({ kind: 'token', text: match[0] });
|
|
290
|
-
lastIndex = match.index + match[0].length;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (lastIndex < text.length) {
|
|
294
|
-
segments.push({ kind: 'text', text: text.slice(lastIndex) });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return segments;
|
|
342
|
+
export const templateTooltip = (
|
|
343
|
+
templates: IReqoreFormTemplates | undefined,
|
|
344
|
+
value: string
|
|
345
|
+
): TReqoreTooltipProp => {
|
|
346
|
+
const { label, item } = describeTemplateReference(templates, value);
|
|
347
|
+
const description = (item as { description?: unknown } | undefined)?.description;
|
|
348
|
+
return typeof description === 'string' && description.trim() ?
|
|
349
|
+
{ title: label, content: description, maxWidth: '360px' }
|
|
350
|
+
: label || value;
|
|
298
351
|
};
|
|
299
352
|
|
|
300
353
|
// Ported verbatim from qorus-ide `helpers/functions.tsx` (FIELD_STACK_REPORT
|
|
@@ -506,7 +559,7 @@ export const buildTemplates = (
|
|
|
506
559
|
|
|
507
560
|
return item;
|
|
508
561
|
}
|
|
509
|
-
);
|
|
562
|
+
).sort(byLabel);
|
|
510
563
|
|
|
511
564
|
return {
|
|
512
565
|
items: Object.values(templates).map(
|
|
@@ -544,6 +597,6 @@ export const buildTemplates = (
|
|
|
544
597
|
data_role
|
|
545
598
|
),
|
|
546
599
|
})
|
|
547
|
-
),
|
|
600
|
+
).sort(byLabel),
|
|
548
601
|
} as IReqoreFormTemplates;
|
|
549
602
|
};
|
|
@@ -1064,14 +1064,39 @@ export const _validateField = (
|
|
|
1064
1064
|
}
|
|
1065
1065
|
}
|
|
1066
1066
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1067
|
+
/* A FALSY value is not an ABSENT one. `null`, `0`, `false` and `""` are
|
|
1068
|
+
all values an author may hold in an `auto`/`any` field — a test
|
|
1069
|
+
assertion expecting a count of zero, a flag that is off, or a blank
|
|
1070
|
+
string — and the truthiness gate that used to stand here refused every
|
|
1071
|
+
one of them as *"Value is empty"*. It surfaced on an assertion's
|
|
1072
|
+
Expected Value holding `null`: opening it reported *"Value for argument
|
|
1073
|
+
1 ("any") is invalid: Value is empty"* on the value the author had
|
|
1074
|
+
deliberately chosen. Only `undefined` is actually empty.
|
|
1075
|
+
|
|
1076
|
+
`null` and `""` are answered here rather than delegated, for two
|
|
1077
|
+
different reasons: `getTypeFromValue(null)` answers `auto`, which would
|
|
1078
|
+
re-enter this very branch, and the `string` validator rejects `""`
|
|
1079
|
+
because that is the rule for a REQUIRED text field — a different
|
|
1080
|
+
question from whether an untyped field may hold a blank string. */
|
|
1081
|
+
if (parsedData === undefined) {
|
|
1082
|
+
return invalidResult('Value is empty');
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (parsedData === null || parsedData === '') {
|
|
1086
|
+
return validResult();
|
|
1072
1087
|
}
|
|
1073
1088
|
|
|
1074
|
-
|
|
1089
|
+
/* Validate what was PARSED, against the type detected from the same
|
|
1090
|
+
parse. Validating the raw `value` instead made the two disagree
|
|
1091
|
+
whenever parsing changed the shape: typing `1` into an `auto` field
|
|
1092
|
+
parsed to the number 1, detected `int`, and then validated the STRING
|
|
1093
|
+
"1" against it — "Value must be an integer", on a value that is one.
|
|
1094
|
+
The author saw their field go invalid on the first character they
|
|
1095
|
+
typed. */
|
|
1096
|
+
return withContext(
|
|
1097
|
+
validateFieldWithResult(getTypeFromValue(parsedData), parsedData),
|
|
1098
|
+
'Auto-detected type is invalid'
|
|
1099
|
+
);
|
|
1075
1100
|
}
|
|
1076
1101
|
case 'processor': {
|
|
1077
1102
|
if (!value || !value['processor-input-type'] || !value['processor-output-type']) {
|
|
@@ -1295,6 +1320,22 @@ export const _validateField = (
|
|
|
1295
1320
|
continue;
|
|
1296
1321
|
}
|
|
1297
1322
|
|
|
1323
|
+
/* An option whose dependencies are not fulfilled is not part of this
|
|
1324
|
+
object, so it cannot make the object invalid.
|
|
1325
|
+
|
|
1326
|
+
The form already takes that view — it HIDES such an option — and
|
|
1327
|
+
the two disagreeing is a trap with no way out of it: a test whose
|
|
1328
|
+
subject moved from a versioned kind to a service keeps the
|
|
1329
|
+
`subject_iface_version` it no longer has a field for, and Submit
|
|
1330
|
+
goes dead naming an option that is nowhere on screen. Nulling the
|
|
1331
|
+
value does not help either, because a null is still a value the
|
|
1332
|
+
loop reaches.
|
|
1333
|
+
|
|
1334
|
+
A REQUIRED option with unfulfilled dependencies is unaffected:
|
|
1335
|
+
`getUnresolvedRequiredOptions` above reports it as `dependency`
|
|
1336
|
+
before this loop runs, and it is the only owner of that question.
|
|
1337
|
+
This branch was therefore only ever able to fail an option the
|
|
1338
|
+
author could not see and did not need. */
|
|
1298
1339
|
if (
|
|
1299
1340
|
(optionSchema?.[option] as TQorusFormFieldSchema)?.depends_on &&
|
|
1300
1341
|
!hasAllDependenciesFullfilled(
|
|
@@ -1303,7 +1344,7 @@ export const _validateField = (
|
|
|
1303
1344
|
optionSchema
|
|
1304
1345
|
)
|
|
1305
1346
|
) {
|
|
1306
|
-
|
|
1347
|
+
continue;
|
|
1307
1348
|
}
|
|
1308
1349
|
|
|
1309
1350
|
const optionResult =
|
|
@@ -1512,6 +1553,31 @@ export const _validateField = (
|
|
|
1512
1553
|
const argDefinition = expressionDefinition.varargs
|
|
1513
1554
|
? expressionDefinition.args[0]
|
|
1514
1555
|
: expressionDefinition.args[index];
|
|
1556
|
+
/* The argument's name for the message.
|
|
1557
|
+
|
|
1558
|
+
`display_name` is what a catalogue MAY carry; the served one carries
|
|
1559
|
+
`name`, so every message about an argument read "argument 1
|
|
1560
|
+
("undefined") is invalid" and named a field that does not exist. */
|
|
1561
|
+
const argLabel =
|
|
1562
|
+
argDefinition?.display_name ?? argDefinition?.name ?? `argument ${index + 1}`;
|
|
1563
|
+
|
|
1564
|
+
/* An explicit null is a VALUE, and DPQL says so: `null` is a literal
|
|
1565
|
+
that parses, serializes and round-trips like any other.
|
|
1566
|
+
|
|
1567
|
+
Treated as a missing one, it made "this returns no value"
|
|
1568
|
+
unsayable. Typing `null` into the text editor produces the literal
|
|
1569
|
+
expression `{exp: "value", args: [null]}`; the visual view then
|
|
1570
|
+
called it invalid and refused to render a summary, and a raw null
|
|
1571
|
+
argument crashed the validator outright on `argValue.type`. Reported
|
|
1572
|
+
from the live IDE by an author with no other way to assert that a
|
|
1573
|
+
service method returns nothing.
|
|
1574
|
+
|
|
1575
|
+
Only an EXPLICIT null counts. An argument that is simply absent, or
|
|
1576
|
+
an envelope holding `undefined`, is still missing — which is the
|
|
1577
|
+
distinction the author is making when they write it. */
|
|
1578
|
+
if (argValue === null || (isObject(argValue) && (argValue as any).value === null)) {
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1515
1581
|
|
|
1516
1582
|
if (!argValue?.value && !argDefinition?.required) {
|
|
1517
1583
|
continue;
|
|
@@ -1526,30 +1592,34 @@ export const _validateField = (
|
|
|
1526
1592
|
if (!result.isValid) {
|
|
1527
1593
|
return withContext(
|
|
1528
1594
|
result,
|
|
1529
|
-
`Sub-expression for argument ${index + 1} ("${
|
|
1595
|
+
`Sub-expression for argument ${index + 1} ("${argLabel}") is invalid`
|
|
1530
1596
|
);
|
|
1531
1597
|
}
|
|
1532
1598
|
|
|
1533
1599
|
continue;
|
|
1534
1600
|
}
|
|
1535
1601
|
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1602
|
+
/* A slot with no operand at all is judged as what it is — a missing
|
|
1603
|
+
value — rather than read for a `type` it does not have.
|
|
1604
|
+
|
|
1605
|
+
An operand that HOLDS something is judged by the envelope's own type,
|
|
1606
|
+
or — when the envelope carries none (a raw parse operand widened
|
|
1607
|
+
without a `ui_type`, a slot reset by a type change) — by the type the
|
|
1608
|
+
catalogue declares. Without that fallback `_validateField` refuses it
|
|
1609
|
+
for "Missing type" however right the value is. */
|
|
1610
|
+
const operandType =
|
|
1611
|
+
argValue?.type ?? (argValue?.value === undefined ? undefined : argDefinition?.ui_type);
|
|
1612
|
+
const result = validateFieldWithResult(operandType, argValue?.value, {
|
|
1613
|
+
expressions,
|
|
1614
|
+
allowed_values: argDefinition?.allowed_values,
|
|
1615
|
+
element_allowed_values: argDefinition?.element_allowed_values,
|
|
1616
|
+
has_to_have_value: argDefinition?.required,
|
|
1617
|
+
});
|
|
1548
1618
|
|
|
1549
1619
|
if (!result.isValid) {
|
|
1550
1620
|
return withContext(
|
|
1551
1621
|
result,
|
|
1552
|
-
`Value for argument ${index + 1} ("${
|
|
1622
|
+
`Value for argument ${index + 1} ("${argLabel}") is invalid`
|
|
1553
1623
|
);
|
|
1554
1624
|
}
|
|
1555
1625
|
}
|
|
@@ -1818,19 +1888,28 @@ export const getUnresolvedRequiredOptions = (
|
|
|
1818
1888
|
/**
|
|
1819
1889
|
* Split a dependency entry into the sibling it names and how its value is compared.
|
|
1820
1890
|
*
|
|
1821
|
-
*
|
|
1822
|
-
* have exactly that value) and `name!=value` (it must
|
|
1823
|
-
* one). The negative
|
|
1824
|
-
* record but one — spelling that as an `any-of`
|
|
1825
|
-
* unreadable in the "Depends on" lock and
|
|
1826
|
-
* added.
|
|
1891
|
+
* Four forms: `name` (the sibling must simply have a value), `!name` (it must have
|
|
1892
|
+
* none), `name=value` (it must have exactly that value) and `name!=value` (it must
|
|
1893
|
+
* have a value, and not that one). The negative comparison is what lets a field say
|
|
1894
|
+
* it applies to every variant of a record but one — spelling that as an `any-of`
|
|
1895
|
+
* list of every other variant is both unreadable in the "Depends on" lock and
|
|
1896
|
+
* silently wrong the moment a variant is added.
|
|
1827
1897
|
*
|
|
1828
1898
|
* `!=` deliberately requires the sibling to be answered: "not X" is a statement about
|
|
1829
1899
|
* an answer, so an unanswered sibling does not satisfy it and the field stays locked.
|
|
1900
|
+
*
|
|
1901
|
+
* `!name` is the form that statement cannot make. Two options that exclude one
|
|
1902
|
+
* another (a converter that reads NodeSet2 and one that writes OpenAPI 3) each
|
|
1903
|
+
* apply only while the OTHER has not been answered at all, and `name!=value` cannot
|
|
1904
|
+
* say that: it requires an answer. Being a predicate like any other, it composes
|
|
1905
|
+
* with the AND of the top-level entries and the OR of a nested list for free.
|
|
1906
|
+
*
|
|
1907
|
+
* `!` binds to the whole entry, so `!name` never carries a value; `!=` is checked
|
|
1908
|
+
* first, which is what keeps `name!=value` from being read as a leading `!`.
|
|
1830
1909
|
*/
|
|
1831
1910
|
export const parseDependency = (
|
|
1832
1911
|
dependency: string
|
|
1833
|
-
): { name: string; op?: '=' | '!='; value?: string } => {
|
|
1912
|
+
): { name: string; op?: '=' | '!=' | '!'; value?: string } => {
|
|
1834
1913
|
const neqIdx = dependency.indexOf('!=');
|
|
1835
1914
|
if (neqIdx !== -1) {
|
|
1836
1915
|
return {
|
|
@@ -1849,9 +1928,25 @@ export const parseDependency = (
|
|
|
1849
1928
|
};
|
|
1850
1929
|
}
|
|
1851
1930
|
|
|
1931
|
+
if (dependency.startsWith('!')) {
|
|
1932
|
+
return { name: dependency.substring(1), op: '!' };
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1852
1935
|
return { name: dependency };
|
|
1853
1936
|
};
|
|
1854
1937
|
|
|
1938
|
+
/** Whether the named sibling currently holds a value this form accepts. */
|
|
1939
|
+
const siblingHasValue = (
|
|
1940
|
+
name: string,
|
|
1941
|
+
options: TQorusForm,
|
|
1942
|
+
optionsSchema?: IQorusFormSchema
|
|
1943
|
+
): boolean =>
|
|
1944
|
+
validateField(options?.[name]?.type, options?.[name]?.value, {
|
|
1945
|
+
...(optionsSchema?.[name] as unknown as IFieldValidationProps),
|
|
1946
|
+
options,
|
|
1947
|
+
optionSchema: optionsSchema,
|
|
1948
|
+
});
|
|
1949
|
+
|
|
1855
1950
|
/** Whether one dependency entry holds against the current form values. */
|
|
1856
1951
|
const isDependencyFulfilled = (
|
|
1857
1952
|
dependency: string,
|
|
@@ -1860,6 +1955,15 @@ const isDependencyFulfilled = (
|
|
|
1860
1955
|
): boolean => {
|
|
1861
1956
|
const { name, op, value } = parseDependency(dependency);
|
|
1862
1957
|
|
|
1958
|
+
if (op === '!') {
|
|
1959
|
+
// "Has no value" is true of a field the form has not materialized at all —
|
|
1960
|
+
// there is nothing there to be the value. So an absent sibling satisfies
|
|
1961
|
+
// this, exactly as it satisfies the bare form below: neither has an answer
|
|
1962
|
+
// to judge, and a mutual exclusion that locked both halves until one of
|
|
1963
|
+
// them was added would offer the author no way in.
|
|
1964
|
+
return !options?.[name] || !siblingHasValue(name, options, optionsSchema);
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1863
1967
|
if (op) {
|
|
1864
1968
|
const optValue = options?.[name]?.value;
|
|
1865
1969
|
if (optValue == null) {
|
|
@@ -1868,17 +1972,12 @@ const isDependencyFulfilled = (
|
|
|
1868
1972
|
return op === '=' ? String(optValue) === value : String(optValue) !== value;
|
|
1869
1973
|
}
|
|
1870
1974
|
|
|
1871
|
-
return options?.[name]
|
|
1872
|
-
? validateField(options[name].type, options[name].value, {
|
|
1873
|
-
...(optionsSchema?.[name] as unknown as IFieldValidationProps),
|
|
1874
|
-
options,
|
|
1875
|
-
optionSchema: optionsSchema,
|
|
1876
|
-
})
|
|
1877
|
-
: true;
|
|
1975
|
+
return options?.[name] ? siblingHasValue(name, options, optionsSchema) : true;
|
|
1878
1976
|
};
|
|
1879
1977
|
|
|
1880
1978
|
export const hasAllDependenciesFullfilled = (
|
|
1881
|
-
|
|
1979
|
+
/** Every entry must hold; a nested list is an ANY of its entries. Mixed freely. */
|
|
1980
|
+
dependencies: ReadonlyArray<string | readonly string[]>,
|
|
1882
1981
|
options: TQorusForm,
|
|
1883
1982
|
optionsSchema?: IQorusFormSchema
|
|
1884
1983
|
): boolean => {
|
|
@@ -1886,20 +1985,20 @@ export const hasAllDependenciesFullfilled = (
|
|
|
1886
1985
|
return true;
|
|
1887
1986
|
}
|
|
1888
1987
|
|
|
1889
|
-
return dependencies.every((dependency
|
|
1988
|
+
return dependencies.every((dependency) => {
|
|
1890
1989
|
// A nested list is an ANY. It used to look each entry up as a whole form-field
|
|
1891
1990
|
// name, so a `name=value` entry inside one found no field and returned `true`
|
|
1892
1991
|
// unconditionally — the any-of group was satisfied by anything at all, while
|
|
1893
1992
|
// CompactRow's lock rendered those same entries as real comparisons. Both halves
|
|
1894
1993
|
// now go through one parser, so the grammar cannot mean two things.
|
|
1895
1994
|
if (isArray(dependency)) {
|
|
1896
|
-
return (dependency as string[]).some((dep) =>
|
|
1995
|
+
return (dependency as readonly string[]).some((dep) =>
|
|
1897
1996
|
isDependencyFulfilled(dep, options, optionsSchema)
|
|
1898
1997
|
);
|
|
1899
1998
|
}
|
|
1900
1999
|
|
|
1901
2000
|
return isString(dependency)
|
|
1902
|
-
? isDependencyFulfilled(dependency
|
|
2001
|
+
? isDependencyFulfilled(dependency, options, optionsSchema)
|
|
1903
2002
|
: true;
|
|
1904
2003
|
});
|
|
1905
2004
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReqoreSpinner, ReqoreTree } from '@qoretechnologies/reqore';
|
|
2
2
|
import { StoryObj } from '@storybook/react-vite';
|
|
3
3
|
import { testsWaitForText } from '../../../__tests__/utils';
|
|
4
|
+
import { storyApiUrl } from '../../stories/storyNetwork';
|
|
4
5
|
import { StoryMeta } from '../../types';
|
|
5
6
|
import { useFetch } from './useFetch';
|
|
6
7
|
|
|
@@ -44,7 +45,7 @@ export const Get: Story = {
|
|
|
44
45
|
mockData: [
|
|
45
46
|
{
|
|
46
47
|
// An array of mock objects which will add in every story
|
|
47
|
-
url: '
|
|
48
|
+
url: storyApiUrl('public/info'),
|
|
48
49
|
method: 'GET',
|
|
49
50
|
status: 200,
|
|
50
51
|
delay: 200,
|
|
@@ -81,7 +82,7 @@ export const Put: Story = {
|
|
|
81
82
|
mockData: [
|
|
82
83
|
{
|
|
83
84
|
// An array of mock objects which will add in every story
|
|
84
|
-
url: '
|
|
85
|
+
url: storyApiUrl('public/info'),
|
|
85
86
|
method: 'PUT',
|
|
86
87
|
status: 200,
|
|
87
88
|
response: {
|
|
@@ -109,7 +110,7 @@ export const Post: Story = {
|
|
|
109
110
|
mockData: [
|
|
110
111
|
{
|
|
111
112
|
// An array of mock objects which will add in every story
|
|
112
|
-
url: '
|
|
113
|
+
url: storyApiUrl('public/info'),
|
|
113
114
|
method: 'POST',
|
|
114
115
|
status: 200,
|
|
115
116
|
response: {
|
|
@@ -137,7 +138,7 @@ export const Del: Story = {
|
|
|
137
138
|
mockData: [
|
|
138
139
|
{
|
|
139
140
|
// An array of mock objects which will add in every story
|
|
140
|
-
url: '
|
|
141
|
+
url: storyApiUrl('public/info'),
|
|
141
142
|
method: 'DELETE',
|
|
142
143
|
status: 200,
|
|
143
144
|
response: {
|
|
@@ -165,7 +166,7 @@ export const Error409: Story = {
|
|
|
165
166
|
mockData: [
|
|
166
167
|
{
|
|
167
168
|
// An array of mock objects which will add in every story
|
|
168
|
-
url: '
|
|
169
|
+
url: storyApiUrl('public/info'),
|
|
169
170
|
method: 'POST',
|
|
170
171
|
status: 409,
|
|
171
172
|
response: {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { QueryClient, useQueryClient } from '@tanstack/react-query';
|
|
1
2
|
import { useCallback, useMemo, useState } from 'react';
|
|
2
3
|
import { useEffectOnce } from 'react-use';
|
|
3
4
|
import { useContextSelector } from 'use-context-selector';
|
|
4
5
|
import { FetchContext } from '../../contexts/FetchContext';
|
|
5
|
-
import {
|
|
6
|
+
import { ReqraftQueryClient } from '../../providers/ReqraftProvider';
|
|
7
|
+
import { IReqraftQueryConfig, reqraftCacheKey } from '../../utils/fetch';
|
|
6
8
|
|
|
7
9
|
export interface IReqraftUseFetch<T> {
|
|
8
10
|
data: T | undefined;
|
|
@@ -41,8 +43,40 @@ export function useFetch<T>({
|
|
|
41
43
|
}
|
|
42
44
|
});
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
/* An answer we ALREADY HAVE is not a loading state.
|
|
47
|
+
`loading` started at `loadOnMount` unconditionally, so every consumer that
|
|
48
|
+
mounted reported "loading" for at least one async turn even when the
|
|
49
|
+
response was sitting in the cache — and `FormEngine` renders its
|
|
50
|
+
`options-loading-skeleton` whenever any of its sources says that. So each
|
|
51
|
+
form appearing on a page flashed a skeleton whether or not anything was
|
|
52
|
+
actually fetched, which is what the repeated skeletons on an IDE page are:
|
|
53
|
+
measured on a fully warm SECOND visit to one page, three skeleton waves
|
|
54
|
+
against two requests, the last wave with no request anywhere near it.
|
|
55
|
+
Seeded from the query cache instead, so a resource already in hand renders
|
|
56
|
+
immediately. */
|
|
57
|
+
/* The provider is OPTIONAL here. `useQueryClient` THROWS when no
|
|
58
|
+
`QueryClientProvider` is above it, and plenty of consumers mount a form
|
|
59
|
+
with only a `FetchContext` — requiring one turned a rendering optimisation
|
|
60
|
+
into a hard dependency and took 131 tests down with it. `useContext` runs
|
|
61
|
+
before that throw, so hook order is unaffected and catching is safe. The
|
|
62
|
+
fallback is the same client `query` itself defaults to, so a cache hit is
|
|
63
|
+
found either way. */
|
|
64
|
+
let queryClient: QueryClient;
|
|
65
|
+
try {
|
|
66
|
+
queryClient = useQueryClient();
|
|
67
|
+
} catch {
|
|
68
|
+
queryClient = ReqraftQueryClient;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const cached = queryClient.getQueryData([
|
|
72
|
+
reqraftCacheKey({ url, method, body, noApiPrefix }),
|
|
73
|
+
]) as { data?: T; ok?: boolean } | undefined;
|
|
74
|
+
const hasCachedAnswer = !!cached && cached.ok !== false && cached.data !== undefined;
|
|
75
|
+
|
|
76
|
+
const [loading, setLoading] = useState(!!loadOnMount && !hasCachedAnswer);
|
|
77
|
+
const [data, setData] = useState<T | undefined>(
|
|
78
|
+
hasCachedAnswer ? (cached?.data as T) : defaultData
|
|
79
|
+
);
|
|
46
80
|
const [response, setResponse] = useState<Response | undefined>();
|
|
47
81
|
const [error, setError] = useState<Error | undefined>();
|
|
48
82
|
const [errorData, setErrorData] = useState<any>();
|
|
@@ -51,13 +85,23 @@ export function useFetch<T>({
|
|
|
51
85
|
async ({
|
|
52
86
|
body: customBody,
|
|
53
87
|
mergeBodies,
|
|
54
|
-
|
|
55
|
-
|
|
88
|
+
silent,
|
|
89
|
+
}: {
|
|
90
|
+
body?: Record<string | number, any>;
|
|
91
|
+
mergeBodies?: boolean;
|
|
92
|
+
/** Revalidate without announcing a loading state — see the note above. */
|
|
93
|
+
silent?: boolean;
|
|
94
|
+
} = {}) => {
|
|
95
|
+
if (!silent) {
|
|
96
|
+
setLoading(true);
|
|
97
|
+
}
|
|
56
98
|
|
|
57
99
|
const _body = mergeBodies ? { ...body, ...customBody } : customBody || body;
|
|
58
100
|
const response = await query<T>({ url, body: _body, cache, noApiPrefix });
|
|
59
101
|
|
|
60
|
-
|
|
102
|
+
if (!silent) {
|
|
103
|
+
setLoading(false);
|
|
104
|
+
}
|
|
61
105
|
|
|
62
106
|
if (response.ok) {
|
|
63
107
|
setError(undefined);
|
|
@@ -75,7 +119,9 @@ export function useFetch<T>({
|
|
|
75
119
|
|
|
76
120
|
useEffectOnce(() => {
|
|
77
121
|
if (loadOnMount) {
|
|
78
|
-
|
|
122
|
+
// Still revalidates when the entry is cached — `query` honours its own
|
|
123
|
+
// staleness — but silently, so a cached answer never flashes a skeleton.
|
|
124
|
+
load({ silent: hasCachedAnswer });
|
|
79
125
|
}
|
|
80
126
|
});
|
|
81
127
|
|
|
@@ -4,6 +4,7 @@ import { useRef } from 'react';
|
|
|
4
4
|
import { fireEvent, within } from 'storybook/test';
|
|
5
5
|
import { storiesStorageMock } from '../../../__tests__/ mock';
|
|
6
6
|
import { testsWaitForText } from '../../../__tests__/utils';
|
|
7
|
+
import { storyApiUrl } from '../../stories/storyNetwork';
|
|
7
8
|
import { StoryMeta } from '../../types';
|
|
8
9
|
import { useReqraftStorage } from './useStorage';
|
|
9
10
|
|
|
@@ -44,7 +45,7 @@ export const DefaultValue: Story = {
|
|
|
44
45
|
},
|
|
45
46
|
mockData: [
|
|
46
47
|
{
|
|
47
|
-
url: '
|
|
48
|
+
url: storyApiUrl('users?action=current'),
|
|
48
49
|
method: 'GET',
|
|
49
50
|
status: 200,
|
|
50
51
|
response: {},
|