@qoretechnologies/reqraft 0.10.53 → 0.10.54-pr.115.g9bc96ed
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/FORM_ENGINE_FIELD_MIGRATION.md +56 -1
- package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
- package/design/SMART_EDITOR_UX.md +21 -0
- 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 +192 -45
- 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 +160 -12
- 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/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 +202 -45
- 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 +20 -58
- 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/dpqlMockLanguage.d.ts +76 -0
- package/dist/components/form/expressions/dpqlMockLanguage.d.ts.map +1 -0
- package/dist/components/form/expressions/dpqlMockLanguage.js +525 -0
- package/dist/components/form/expressions/dpqlMockLanguage.js.map +1 -0
- package/dist/components/form/expressions/dpqlMockLsp.d.ts +31 -2
- package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +1 -1
- package/dist/components/form/expressions/dpqlMockLsp.js +86 -80
- package/dist/components/form/expressions/dpqlMockLsp.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/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.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 +32 -8
- 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/rich-text/RichText.d.ts +21 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +140 -8
- 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 +528 -50
- 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/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/__fixtures__/mockLspServer.d.ts +8 -0
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +1 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js +53 -10
- package/dist/components/smartEditor/__fixtures__/mockLspServer.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/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 +3 -3
- 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/templateText.d.ts +37 -0
- package/dist/helpers/templateText.d.ts.map +1 -0
- package/dist/helpers/templateText.js +64 -0
- package/dist/helpers/templateText.js.map +1 -0
- package/dist/helpers/templates.d.ts +19 -11
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +46 -29
- 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 +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 +240 -51
- package/src/components/form/engine/CompactToolbar.tsx +10 -3
- package/src/components/form/engine/FormEngine.stories.tsx +423 -120
- 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/DpqlRendering.tsx +85 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
- package/src/components/form/expressions/ExpressionField.tsx +265 -54
- package/src/components/form/expressions/argumentPresence.ts +200 -0
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +20 -4
- package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
- package/src/components/form/expressions/builder/index.tsx +39 -77
- 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/useRenderExpression.ts +43 -127
- 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.stories.tsx +27 -20
- package/src/components/form/fields/auto/AutoFormField.tsx +33 -9
- package/src/components/form/fields/date/Date.tsx +17 -1
- package/src/components/form/fields/rich-text/RichText.tsx +199 -22
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
- package/src/components/form/fields/template/TemplateField.stories.tsx +31 -9
- package/src/components/form/fields/template/TemplateField.tsx +714 -122
- package/src/components/form/index.tsx +1 -0
- package/src/components/log/Log.stories.tsx +2 -1
- 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/helpers/dpqlDetection.ts +209 -0
- package/src/helpers/options.ts +70 -0
- package/src/helpers/templateItems.ts +81 -0
- package/src/helpers/templateText.ts +92 -0
- package/src/helpers/templates.ts +47 -37
- 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
|
@@ -1,100 +1,126 @@
|
|
|
1
1
|
// Copyright 2026 Qore Technologies, s.r.o.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
2
|
+
// The DPQL language server every story talks to unless it brings its own.
|
|
3
|
+
//
|
|
4
|
+
// `.storybook/preview.tsx` starts it before each story, so an expression field,
|
|
5
|
+
// a Preview or an Explain panel finds a server on the endpoint whatever story it
|
|
6
|
+
// sits in — the same way `GLOBAL_STORY_MOCK_DATA` answers the REST requests
|
|
7
|
+
// components make on their own. A story that needs different handlers starts
|
|
8
|
+
// its own server on the URL and replaces this one (see `createMockLspServer`).
|
|
9
|
+
// The answers come from `dpqlMockLanguage`, which follows the real server.
|
|
8
10
|
import {
|
|
9
11
|
createMockLspServer,
|
|
12
|
+
DEFAULT_LSP_CAPABILITIES,
|
|
13
|
+
IMockLspServer,
|
|
10
14
|
MOCK_LSP_URL,
|
|
15
|
+
TMockLspHandler,
|
|
11
16
|
} from '../../smartEditor/__fixtures__/mockLspServer';
|
|
17
|
+
import {
|
|
18
|
+
IDpqlMockNode,
|
|
19
|
+
mockParseDpql,
|
|
20
|
+
mockRenderDpql,
|
|
21
|
+
mockSerializeDpql,
|
|
22
|
+
mockTokenizeDpql,
|
|
23
|
+
} from './dpqlMockLanguage';
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Every `dpql/parse` the mock answered, in order, for diagnosis.
|
|
27
|
+
*
|
|
28
|
+
* The type-analysis stories depend on the front end SENDING `target_type` —
|
|
29
|
+
* the mock returns analysis only when it is present, exactly as the server
|
|
30
|
+
* behaves. When one of those stories fails there are two very different
|
|
31
|
+
* causes, and the rendered DOM cannot tell them apart: the request never
|
|
32
|
+
* carried a target, or it did and the answer was not rendered. This records
|
|
33
|
+
* which, and the story reports it in its assertion message so the answer
|
|
34
|
+
* survives into a CI log.
|
|
35
|
+
*/
|
|
36
|
+
export interface IDpqlMockParseCall {
|
|
37
|
+
text: string;
|
|
38
|
+
target?: string;
|
|
39
|
+
analysed: boolean;
|
|
20
40
|
}
|
|
41
|
+
export const dpqlMockParseCalls: IDpqlMockParseCall[] = [];
|
|
21
42
|
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
/** The template namespaces the server offers after a `$`. */
|
|
44
|
+
const DPQL_MOCK_TEMPLATE_ITEMS = [
|
|
45
|
+
{ label: '$data:', insertText: '$data:', kind: 1, detail: 'template' },
|
|
46
|
+
{ label: '$config:', insertText: '$config:', kind: 1, detail: 'template' },
|
|
47
|
+
{ label: '$static:', insertText: '$static:', kind: 1, detail: 'template' },
|
|
48
|
+
{ label: '$timestamp:', insertText: '$timestamp:', kind: 1, detail: 'template' },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/** The record fields the server offers after an `@`. */
|
|
52
|
+
const DPQL_MOCK_FIELD_ITEMS = [
|
|
53
|
+
{ label: '@name', insertText: '@name', kind: 5, detail: 'string' },
|
|
54
|
+
{ label: '@status', insertText: '@status', kind: 5, detail: 'string' },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const DPQL_MOCK_HANDLERS: Record<string, TMockLspHandler> = {
|
|
58
|
+
/* Position-aware, as the real server is: `$` opens the template
|
|
59
|
+
namespaces, `@` the record fields. Answering the same list whatever
|
|
60
|
+
precedes the cursor would make a story asserting the `$` menu pass
|
|
61
|
+
for a shell that asked in the wrong context — which is the only thing
|
|
62
|
+
such a story is there to catch. (`DpqlEditor`'s own stories carry the
|
|
63
|
+
full catalogue; this is the minimal subset.) */
|
|
64
|
+
'textDocument/completion': (msg, server) => {
|
|
65
|
+
const position = msg.params?.position ?? { line: 0, character: 0 };
|
|
66
|
+
const line = server.textOf(msg.params?.textDocument?.uri).split('\n')[position.line] ?? '';
|
|
67
|
+
const head = line.slice(0, position.character);
|
|
68
|
+
const sigil = head.match(/[@$][\w:.{}]*$/)?.[0]?.[0] ?? '';
|
|
69
|
+
if (sigil === '$') {
|
|
70
|
+
return { isIncomplete: false, items: DPQL_MOCK_TEMPLATE_ITEMS };
|
|
71
|
+
}
|
|
72
|
+
if (sigil === '@') {
|
|
73
|
+
return { isIncomplete: false, items: DPQL_MOCK_FIELD_ITEMS };
|
|
74
|
+
}
|
|
75
|
+
return { isIncomplete: false, items: [] };
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
'textDocument/semanticTokens/full': (msg, server) => ({
|
|
79
|
+
data: mockTokenizeDpql(server.textOf(msg.params?.textDocument?.uri)),
|
|
80
|
+
}),
|
|
33
81
|
|
|
34
|
-
|
|
82
|
+
'dpql/setContext': () => ({ fields: {} }),
|
|
35
83
|
|
|
36
|
-
|
|
84
|
+
'dpql/parse': (msg) => {
|
|
85
|
+
const text = String(msg.params?.text ?? '');
|
|
86
|
+
const target = msg.params?.target_type as string | undefined;
|
|
87
|
+
dpqlMockParseCalls.push({ text: text.trim(), target, analysed: !!target });
|
|
88
|
+
return mockParseDpql(text, target);
|
|
89
|
+
},
|
|
37
90
|
|
|
38
|
-
|
|
39
|
-
// deterministic and verifiable.
|
|
40
|
-
'dpql/parse': (msg) => {
|
|
41
|
-
const text = (msg.params?.text ?? '').trim();
|
|
42
|
-
return {
|
|
43
|
-
success: true,
|
|
44
|
-
expression: {
|
|
45
|
-
is_expression: true,
|
|
46
|
-
value: {
|
|
47
|
-
exp: '==',
|
|
48
|
-
args: [
|
|
49
|
-
{ type: 'string', value: text || 'x' },
|
|
50
|
-
{ type: 'string', value: '' },
|
|
51
|
-
],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
diagnostics: [],
|
|
55
|
-
};
|
|
56
|
-
},
|
|
91
|
+
'dpql/serialize': (msg) => mockSerializeDpql(msg.params?.expression as IDpqlMockNode),
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
const expr = msg.params?.expression as IMockExprNode | undefined;
|
|
60
|
-
const args = expr?.args ?? expr?.value?.args ?? [];
|
|
61
|
-
const left = args[0]?.value ?? '';
|
|
62
|
-
const right = args[1]?.value ?? '';
|
|
63
|
-
return { dpql: `${left} == ${right}`.trim() };
|
|
64
|
-
},
|
|
93
|
+
'dpql/validate': () => ({ diagnostics: [] }),
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
'dpql/renderExpression': (msg) => mockRenderDpql(msg.params?.expression as IDpqlMockNode),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export interface IStartDpqlMockLspOptions {
|
|
99
|
+
/** Handlers to use instead of, or beside, the default ones. */
|
|
100
|
+
handlers?: Record<string, TMockLspHandler>;
|
|
101
|
+
/** Per-method response delays in ms. */
|
|
102
|
+
delays?: Record<string, number>;
|
|
103
|
+
}
|
|
67
104
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
? `(${renderNode(arg.value)})`
|
|
79
|
-
: typeof arg?.value === 'string'
|
|
80
|
-
? JSON.stringify(arg.value)
|
|
81
|
-
: String(arg?.value)
|
|
82
|
-
);
|
|
83
|
-
return /^[^\w\s]+$/.test(node.exp)
|
|
84
|
-
? args.join(` ${node.exp} `)
|
|
85
|
-
: `${node.exp}(${args.join(', ')})`;
|
|
86
|
-
};
|
|
87
|
-
const rendered = renderNode(root);
|
|
88
|
-
return {
|
|
89
|
-
rendered,
|
|
90
|
-
richtext: {
|
|
91
|
-
type: 'richtext',
|
|
92
|
-
value: [{ type: 'paragraph', children: [{ text: rendered }] }],
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
},
|
|
105
|
+
/** Start the mock DPQL language server; returns the server and a teardown. */
|
|
106
|
+
export const startDpqlMockLspServer = (
|
|
107
|
+
options: IStartDpqlMockLspOptions = {}
|
|
108
|
+
): { lsp: IMockLspServer; stop: () => void } => {
|
|
109
|
+
dpqlMockParseCalls.length = 0;
|
|
110
|
+
const lsp = createMockLspServer(MOCK_LSP_URL, {
|
|
111
|
+
capabilities: {
|
|
112
|
+
...DEFAULT_LSP_CAPABILITIES,
|
|
113
|
+
textDocumentSync: { openClose: true, change: 2 },
|
|
114
|
+
completionProvider: { triggerCharacters: ['@', '$', '.', ':'] },
|
|
96
115
|
},
|
|
116
|
+
// Unknown requests get an empty success so nothing hangs.
|
|
117
|
+
defaultResult: {},
|
|
118
|
+
delays: options.delays,
|
|
119
|
+
handlers: { ...DPQL_MOCK_HANDLERS, ...options.handlers },
|
|
97
120
|
});
|
|
98
|
-
|
|
99
|
-
return () => lsp.close();
|
|
121
|
+
return { lsp, stop: () => lsp.close() };
|
|
100
122
|
};
|
|
123
|
+
|
|
124
|
+
/** Start the mock DPQL language server; returns a teardown function. */
|
|
125
|
+
export const startDpqlMockLsp = (options?: IStartDpqlMockLspOptions): (() => void) =>
|
|
126
|
+
startDpqlMockLspServer(options).stop;
|
|
@@ -119,7 +119,8 @@ export const mockExpressions: IExpressionSchema[] = [
|
|
|
119
119
|
return_type: 'bool',
|
|
120
120
|
ui_return_type: 'bool',
|
|
121
121
|
varargs: false,
|
|
122
|
-
groups: ['
|
|
122
|
+
groups: ['Comparison'],
|
|
123
|
+
render_template: '$arg[0] contains $arg[1]${arg[2] ? (ignore case):}',
|
|
123
124
|
args: stringArgs('Substring', 'contains'),
|
|
124
125
|
},
|
|
125
126
|
{
|
|
@@ -133,7 +134,8 @@ export const mockExpressions: IExpressionSchema[] = [
|
|
|
133
134
|
return_type: 'bool',
|
|
134
135
|
ui_return_type: 'bool',
|
|
135
136
|
varargs: false,
|
|
136
|
-
groups: ['
|
|
137
|
+
groups: ['Comparison'],
|
|
138
|
+
render_template: '$arg[0].startsWith($arg[1], $arg[2])',
|
|
137
139
|
args: stringArgs('Start String', 'starts with'),
|
|
138
140
|
},
|
|
139
141
|
{
|
|
@@ -147,7 +149,8 @@ export const mockExpressions: IExpressionSchema[] = [
|
|
|
147
149
|
return_type: 'bool',
|
|
148
150
|
ui_return_type: 'bool',
|
|
149
151
|
varargs: false,
|
|
150
|
-
groups: ['
|
|
152
|
+
groups: ['Comparison'],
|
|
153
|
+
render_template: '$arg[0].endsWith($arg[1], $arg[2])',
|
|
151
154
|
args: stringArgs('End String', 'ends with'),
|
|
152
155
|
},
|
|
153
156
|
// --- Varargs (int) -------------------------------------------------------
|
|
@@ -242,7 +245,8 @@ export const mockExpressions: IExpressionSchema[] = [
|
|
|
242
245
|
return_type: 'bool',
|
|
243
246
|
ui_return_type: 'bool',
|
|
244
247
|
varargs: false,
|
|
245
|
-
groups: ['
|
|
248
|
+
groups: ['Comparison'],
|
|
249
|
+
render_template: '$arg[0] =~ /$arg[1]/$arg[2]',
|
|
246
250
|
args: [
|
|
247
251
|
{ name: 'softstring', display_name: 'String Value', ui_type: 'richtext', required: true },
|
|
248
252
|
{ name: 'string', display_name: 'Regular Expression', ui_type: 'string', required: true },
|
|
@@ -1,15 +1,77 @@
|
|
|
1
1
|
// Copyright 2026 Qore Technologies, s.r.o.
|
|
2
|
-
// Client-side expression renderer —
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// so it stays out of the LSP/nanoid dependency graph.
|
|
2
|
+
// Client-side expression renderer — an approximation of an expression AST for
|
|
3
|
+
// the read-first row summaries, which must render synchronously. Nothing that
|
|
4
|
+
// can wait uses it: Explain and the Text view's Preview wait for the server's
|
|
5
|
+
// `dpql/renderExpression` (see `useRenderExpression`). Pure: no
|
|
6
|
+
// transport/socket import, so it stays out of the LSP/nanoid dependency graph.
|
|
7
7
|
import { IExpression, IExpressionSchema, IExpressionValue } from './types';
|
|
8
8
|
|
|
9
9
|
/** A symbol made only of non-word characters renders infix (`a == b`). */
|
|
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 a row summary that parenthesised
|
|
19
|
+
* differently would make the same expression read two ways depending on
|
|
20
|
+
* whether it was collapsed or open. (It does not model the catalogue's
|
|
21
|
+
* `render_template`s, so `contains` still reads as a call here.)
|
|
22
|
+
*/
|
|
23
|
+
const PRECEDENCE: Record<string, number> = {
|
|
24
|
+
'*': 1,
|
|
25
|
+
'/': 1,
|
|
26
|
+
'%': 1,
|
|
27
|
+
'+': 2,
|
|
28
|
+
'-': 2,
|
|
29
|
+
'!': 3,
|
|
30
|
+
'==': 4,
|
|
31
|
+
'!=': 4,
|
|
32
|
+
'<': 4,
|
|
33
|
+
'<=': 4,
|
|
34
|
+
'>': 4,
|
|
35
|
+
'>=': 4,
|
|
36
|
+
'&&': 5,
|
|
37
|
+
'||': 6,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Anything that is not an operator; nothing binds tighter. */
|
|
41
|
+
const PREC_PRIMARY = 0;
|
|
42
|
+
/** Looser than every operator, so a value rendered in it is never wrapped. */
|
|
43
|
+
const PREC_LOOSEST = 7;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Operators whose grouping cannot change the result.
|
|
47
|
+
*
|
|
48
|
+
* `+` is deliberately absent: it concatenates when an operand is a string, so
|
|
49
|
+
* `1 + (2 + "a")` is `"12a"` where `1 + 2 + "a"` is `"3a"`. Dropping its
|
|
50
|
+
* parentheses would not be a cosmetic simplification.
|
|
51
|
+
*/
|
|
52
|
+
const ASSOCIATIVE = new Set(['&&', '||']);
|
|
53
|
+
|
|
54
|
+
const precedenceOf = (exp?: string): number => PRECEDENCE[exp ?? ''] ?? PREC_PRIMARY;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The context an operator's argument is rendered in — the same rule as
|
|
58
|
+
* `DpqlSerializer::getArgumentContext()`.
|
|
59
|
+
*
|
|
60
|
+
* The first argument is where left-associative parsing puts a same-precedence
|
|
61
|
+
* child back, so it needs no parentheses; a later one does, or `1 - (2 - 3)`
|
|
62
|
+
* reads as `1 - 2 - 3`, which is a different expression.
|
|
63
|
+
*/
|
|
64
|
+
const argumentContext = (exp: string, argn: number): number => {
|
|
65
|
+
const prec = precedenceOf(exp);
|
|
66
|
+
return !argn || ASSOCIATIVE.has(exp) ? prec : prec - 1;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** An argument reaches this as a bare value or as a `{type, value}` envelope. */
|
|
70
|
+
const readArgValue = (arg: unknown): unknown =>
|
|
71
|
+
arg !== null && typeof arg === 'object' && 'value' in (arg as object)
|
|
72
|
+
? (arg as { value: unknown }).value
|
|
73
|
+
: arg;
|
|
74
|
+
|
|
13
75
|
const renderLiteral = (value: unknown): string => {
|
|
14
76
|
if (value === undefined || value === null) return 'null';
|
|
15
77
|
if (typeof value === 'string') return JSON.stringify(value);
|
|
@@ -17,31 +79,59 @@ const renderLiteral = (value: unknown): string => {
|
|
|
17
79
|
return String(value);
|
|
18
80
|
};
|
|
19
81
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
* approximation, used as the fallback when the LSP is unreachable.
|
|
23
|
-
*/
|
|
24
|
-
export const renderExpressionToText = (
|
|
82
|
+
/** Renders `value` and wraps it only if it binds looser than `contextPrec`. */
|
|
83
|
+
const renderInContext = (
|
|
25
84
|
value: IExpressionValue | undefined,
|
|
26
|
-
expressions: IExpressionSchema[]
|
|
85
|
+
expressions: IExpressionSchema[],
|
|
86
|
+
contextPrec: number
|
|
27
87
|
): string => {
|
|
28
88
|
if (!value?.exp) return '';
|
|
89
|
+
/* A `value` expression IS its literal.
|
|
90
|
+
|
|
91
|
+
It is the wrapper the parser puts around a bare literal — typing `null`,
|
|
92
|
+
or `42`, into the text editor produces one — and it carries no symbol, so
|
|
93
|
+
rendering it through the generic paths printed the wrapper instead of the
|
|
94
|
+
value: `value(42)` with a catalogue that names it, `(42)` with one that
|
|
95
|
+
supplies its empty symbol. The server renders the literal alone, and a
|
|
96
|
+
summary that disagrees with the preview beside it is worse than either. */
|
|
97
|
+
if (value.exp === 'value' && (value.args ?? []).length === 1) {
|
|
98
|
+
const only = (value.args ?? [])[0] as IExpression;
|
|
99
|
+
if (!only?.is_expression) {
|
|
100
|
+
return renderLiteral(readArgValue(only));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
29
103
|
const schema = expressions.find((e) => e.name === value.exp);
|
|
30
104
|
const symbol = schema?.symbol ?? value.exp;
|
|
31
|
-
const args =
|
|
105
|
+
const args = value.args ?? [];
|
|
106
|
+
// Logical group (subtype 2) → join with the symbol.
|
|
107
|
+
const isLogicalGroup = schema?.subtype === 2 || value.exp === '&&' || value.exp === '||';
|
|
108
|
+
// Binary operator → infix. Everything else renders as a call, whose own
|
|
109
|
+
// parentheses and commas already delimit its arguments.
|
|
110
|
+
const isInfix = isLogicalGroup || (args.length === 2 && isOperatorSymbol(symbol));
|
|
111
|
+
|
|
112
|
+
const parts = args.map((arg: IExpression, argn: number) =>
|
|
32
113
|
arg?.is_expression && arg.value?.exp
|
|
33
|
-
?
|
|
114
|
+
? renderInContext(
|
|
115
|
+
arg.value,
|
|
116
|
+
expressions,
|
|
117
|
+
isInfix ? argumentContext(value.exp as string, argn) : PREC_LOOSEST
|
|
118
|
+
)
|
|
34
119
|
: renderLiteral(arg?.value)
|
|
35
120
|
);
|
|
36
121
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return args.join(` ${symbol} `);
|
|
40
|
-
}
|
|
41
|
-
// Binary operator → infix.
|
|
42
|
-
if (args.length === 2 && isOperatorSymbol(symbol)) {
|
|
43
|
-
return `${args[0]} ${symbol} ${args[1]}`;
|
|
122
|
+
if (!isInfix) {
|
|
123
|
+
return `${symbol}(${parts.join(', ')})`;
|
|
44
124
|
}
|
|
45
|
-
|
|
46
|
-
|
|
125
|
+
|
|
126
|
+
const text = isLogicalGroup ? parts.join(` ${symbol} `) : `${parts[0]} ${symbol} ${parts[1]}`;
|
|
127
|
+
return precedenceOf(value.exp) > contextPrec ? `(${text})` : text;
|
|
47
128
|
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Recursively render an expression AST to readable text — the client-side
|
|
132
|
+
* approximation a read-first row summary shows.
|
|
133
|
+
*/
|
|
134
|
+
export const renderExpressionToText = (
|
|
135
|
+
value: IExpressionValue | undefined,
|
|
136
|
+
expressions: IExpressionSchema[] = []
|
|
137
|
+
): string => renderInContext(value, expressions, PREC_LOOSEST);
|
|
@@ -68,6 +68,13 @@ export interface IExpressionSchema {
|
|
|
68
68
|
return_type: TQorusType;
|
|
69
69
|
ui_return_type: string;
|
|
70
70
|
symbol: string;
|
|
71
|
+
/**
|
|
72
|
+
* How the server's readable rendering (`dpql/renderExpression`) spells this
|
|
73
|
+
* expression, when not by its symbol: `$arg[n]` is an argument, `$symbol` the
|
|
74
|
+
* symbol, `$args` all arguments, and `${arg[n] ? set : unset}` reads the
|
|
75
|
+
* argument or its `default_value` — e.g. `$arg[0].startsWith($arg[1], $arg[2])`.
|
|
76
|
+
*/
|
|
77
|
+
render_template?: string;
|
|
71
78
|
/** Server type code (operator/function); not a reliable infix discriminator. */
|
|
72
79
|
type: number;
|
|
73
80
|
/** `1` normal, `2` logical group (AND/OR). */
|