@cratis/screenplay-language 0.1.0
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/README.md +52 -0
- package/completion-items.ts +128 -0
- package/completion-planner.ts +111 -0
- package/completions.ts +112 -0
- package/diagnostics.ts +52 -0
- package/dist/completion-items.d.ts +23 -0
- package/dist/completion-items.d.ts.map +1 -0
- package/dist/completion-items.js +105 -0
- package/dist/completion-items.js.map +1 -0
- package/dist/completion-planner.d.ts +25 -0
- package/dist/completion-planner.d.ts.map +1 -0
- package/dist/completion-planner.js +90 -0
- package/dist/completion-planner.js.map +1 -0
- package/dist/completions.d.ts +4 -0
- package/dist/completions.d.ts.map +1 -0
- package/dist/completions.js +78 -0
- package/dist/completions.js.map +1 -0
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +47 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/document-context.d.ts +5 -0
- package/dist/document-context.d.ts.map +1 -0
- package/dist/document-context.js +45 -0
- package/dist/document-context.js.map +1 -0
- package/dist/hover-content.d.ts +2 -0
- package/dist/hover-content.d.ts.map +1 -0
- package/dist/hover-content.js +64 -0
- package/dist/hover-content.js.map +1 -0
- package/dist/hover.d.ts +3 -0
- package/dist/hover.d.ts.map +1 -0
- package/dist/hover.js +15 -0
- package/dist/hover.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/keyword-docs.d.ts +4 -0
- package/dist/keyword-docs.d.ts.map +1 -0
- package/dist/keyword-docs.js +100 -0
- package/dist/keyword-docs.js.map +1 -0
- package/dist/language.d.ts +12 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +117 -0
- package/dist/language.js.map +1 -0
- package/dist/sub-language-registry.d.ts +20 -0
- package/dist/sub-language-registry.d.ts.map +1 -0
- package/dist/sub-language-registry.js +18 -0
- package/dist/sub-language-registry.js.map +1 -0
- package/dist/sub-languages/capture/CompletionProvider.d.ts +32 -0
- package/dist/sub-languages/capture/CompletionProvider.d.ts.map +1 -0
- package/dist/sub-languages/capture/CompletionProvider.js +332 -0
- package/dist/sub-languages/capture/CompletionProvider.js.map +1 -0
- package/dist/sub-languages/capture/HoverProvider.d.ts +8 -0
- package/dist/sub-languages/capture/HoverProvider.d.ts.map +1 -0
- package/dist/sub-languages/capture/HoverProvider.js +87 -0
- package/dist/sub-languages/capture/HoverProvider.js.map +1 -0
- package/dist/sub-languages/capture/Validator.d.ts +6 -0
- package/dist/sub-languages/capture/Validator.d.ts.map +1 -0
- package/dist/sub-languages/capture/Validator.js +72 -0
- package/dist/sub-languages/capture/Validator.js.map +1 -0
- package/dist/sub-languages/capture/index.d.ts +6 -0
- package/dist/sub-languages/capture/index.d.ts.map +1 -0
- package/dist/sub-languages/capture/index.js +58 -0
- package/dist/sub-languages/capture/index.js.map +1 -0
- package/dist/sub-languages/capture/language.d.ts +6 -0
- package/dist/sub-languages/capture/language.d.ts.map +1 -0
- package/dist/sub-languages/capture/language.js +160 -0
- package/dist/sub-languages/capture/language.js.map +1 -0
- package/dist/sub-languages/cdl.d.ts +3 -0
- package/dist/sub-languages/cdl.d.ts.map +1 -0
- package/dist/sub-languages/cdl.js +74 -0
- package/dist/sub-languages/cdl.js.map +1 -0
- package/dist/sub-languages/pdl.d.ts +3 -0
- package/dist/sub-languages/pdl.d.ts.map +1 -0
- package/dist/sub-languages/pdl.js +67 -0
- package/dist/sub-languages/pdl.js.map +1 -0
- package/dist/sub-languages/projection/CodeActionProvider.d.ts +18 -0
- package/dist/sub-languages/projection/CodeActionProvider.d.ts.map +1 -0
- package/dist/sub-languages/projection/CodeActionProvider.js +93 -0
- package/dist/sub-languages/projection/CodeActionProvider.js.map +1 -0
- package/dist/sub-languages/projection/CompletionProvider.d.ts +41 -0
- package/dist/sub-languages/projection/CompletionProvider.d.ts.map +1 -0
- package/dist/sub-languages/projection/CompletionProvider.js +591 -0
- package/dist/sub-languages/projection/CompletionProvider.js.map +1 -0
- package/dist/sub-languages/projection/HoverProvider.d.ts +24 -0
- package/dist/sub-languages/projection/HoverProvider.d.ts.map +1 -0
- package/dist/sub-languages/projection/HoverProvider.js +298 -0
- package/dist/sub-languages/projection/HoverProvider.js.map +1 -0
- package/dist/sub-languages/projection/Validator.d.ts +22 -0
- package/dist/sub-languages/projection/Validator.d.ts.map +1 -0
- package/dist/sub-languages/projection/Validator.js +339 -0
- package/dist/sub-languages/projection/Validator.js.map +1 -0
- package/dist/sub-languages/projection/index.d.ts +49 -0
- package/dist/sub-languages/projection/index.d.ts.map +1 -0
- package/dist/sub-languages/projection/index.js +160 -0
- package/dist/sub-languages/projection/index.js.map +1 -0
- package/dist/sub-languages/projection/language.d.ts +5 -0
- package/dist/sub-languages/projection/language.d.ts.map +1 -0
- package/dist/sub-languages/projection/language.js +171 -0
- package/dist/sub-languages/projection/language.js.map +1 -0
- package/dist/symbols.d.ts +45 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +100 -0
- package/dist/symbols.js.map +1 -0
- package/dist/themes/screenplay-dark.d.ts +4 -0
- package/dist/themes/screenplay-dark.d.ts.map +1 -0
- package/dist/themes/screenplay-dark.js +29 -0
- package/dist/themes/screenplay-dark.js.map +1 -0
- package/dist/themes/screenplay-light.d.ts +4 -0
- package/dist/themes/screenplay-light.d.ts.map +1 -0
- package/dist/themes/screenplay-light.js +29 -0
- package/dist/themes/screenplay-light.js.map +1 -0
- package/dist/tokens.d.ts +5 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +121 -0
- package/dist/tokens.js.map +1 -0
- package/dist/validation.d.ts +10 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +101 -0
- package/dist/validation.js.map +1 -0
- package/document-context.ts +51 -0
- package/hover-content.ts +76 -0
- package/hover.ts +23 -0
- package/index.ts +98 -0
- package/keyword-docs.ts +102 -0
- package/language.ts +130 -0
- package/package.json +44 -0
- package/sub-language-registry.ts +42 -0
- package/sub-languages/capture/CompletionProvider.ts +365 -0
- package/sub-languages/capture/HoverProvider.ts +99 -0
- package/sub-languages/capture/Validator.ts +97 -0
- package/sub-languages/capture/index.ts +72 -0
- package/sub-languages/capture/language.ts +176 -0
- package/sub-languages/cdl.ts +76 -0
- package/sub-languages/pdl.ts +69 -0
- package/sub-languages/projection/CodeActionProvider.ts +123 -0
- package/sub-languages/projection/CompletionProvider.ts +653 -0
- package/sub-languages/projection/HoverProvider.ts +338 -0
- package/sub-languages/projection/Validator.ts +387 -0
- package/sub-languages/projection/index.ts +223 -0
- package/sub-languages/projection/language.ts +193 -0
- package/symbols.ts +161 -0
- package/themes/screenplay-dark.ts +32 -0
- package/themes/screenplay-light.ts +32 -0
- package/tokens.ts +145 -0
- package/validation.ts +145 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @cratis/screenplay-language
|
|
2
|
+
|
|
3
|
+
Monaco language service for the Cratis **Screenplay** DSL (`.play` files) — syntax highlighting, IntelliSense completions, hover documentation, and diagnostics.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import * as monaco from 'monaco-editor';
|
|
9
|
+
import { register } from '@cratis/screenplay-language';
|
|
10
|
+
|
|
11
|
+
register(monaco);
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`register` is the single entry point. It registers the `screenplay` language (`.play` extension), the Monarch tokenizer, the completion and hover providers, the diagnostics watcher, and the `screenplay-dark` / `screenplay-light` themes on the Monaco instance you pass in. `monaco-editor` is a peer dependency — the package never bundles Monaco.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { languageId, screenplayDarkThemeName, screenplayLightThemeName } from '@cratis/screenplay-language';
|
|
18
|
+
|
|
19
|
+
monaco.editor.create(element, {
|
|
20
|
+
language: languageId,
|
|
21
|
+
theme: screenplayDarkThemeName,
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What is covered
|
|
26
|
+
|
|
27
|
+
- **Highlighting** — all construct and clause keywords, slice types, concept attributes (`@pii`, `@sensitive`), context variables (`$context.*`, `$env.*`, `$eventContext.*`, `$.\*`), strings, numbers, and comments.
|
|
28
|
+
- **Embedded code blocks** — `csharp`, `typescript`, `react`, and `html` blocks between triple backticks are highlighted with Monaco's own language grammars.
|
|
29
|
+
- **Sub-languages** — the PDL (`projection`) and CDL (`capture`) bodies are tokenized by their own registered rules using Monarch's state stack.
|
|
30
|
+
- **Completions** — context-aware by enclosing construct (top level, module, feature, slice, command, produces, handler, screen, constraint, …), plus in-scope symbol names: policies after `authorize`, events after `on` and `produces`, concepts and primitives in type positions.
|
|
31
|
+
- **Hover** — keyword documentation, concept definitions (primitive + attributes), policy require expressions, and event property lists.
|
|
32
|
+
- **Diagnostics** — unknown slice types, unknown primitive types, references to undeclared policies and events, tab indentation, and unclosed code fences.
|
|
33
|
+
|
|
34
|
+
## Extending with new sub-languages
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { registerSubLanguage } from '@cratis/screenplay-language';
|
|
38
|
+
|
|
39
|
+
registerSubLanguage('workflow', {
|
|
40
|
+
tokens: [[/\b(?:start|step|end)\b/, 'keyword']],
|
|
41
|
+
completions: [{ label: 'step', insertText: 'step ${1:Name}', documentation: 'A workflow step.' }],
|
|
42
|
+
hovers: { step: 'Workflow — a step executed in sequence.' },
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Registration works before or after `register(monaco)`; the tokenizer recomposes on the fly. See the [sub-language documentation](../../../../Documentation/screenplay/sub-languages.md) for the full design.
|
|
47
|
+
|
|
48
|
+
## Building
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
yarn build
|
|
52
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
export interface CompletionEntry {
|
|
5
|
+
label: string;
|
|
6
|
+
insertText: string;
|
|
7
|
+
documentation: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const fenced = (tag: string) => `${tag}\n\`\`\`\n\${1}\n\`\`\``;
|
|
11
|
+
|
|
12
|
+
export const topLevelItems: CompletionEntry[] = [
|
|
13
|
+
{ label: 'import', insertText: 'import ${1:Module}.${2:Type}', documentation: 'Imports a type from another module by its qualified name.' },
|
|
14
|
+
{ label: 'concept', insertText: 'concept ${1:Name} : ${2|Uuid,String,Int,Decimal,Bool,Date,DateTime|}', documentation: 'Declares a formalized value type wrapping a primitive.' },
|
|
15
|
+
{ label: 'concept (enum)', insertText: 'concept ${1:Name} : Enum\n ${2:value}', documentation: 'Declares an enumeration concept with a fixed set of values.' },
|
|
16
|
+
{ label: 'policy', insertText: 'policy ${1:Name}\n require ${2:authenticated}', documentation: 'Declares a named authorization rule for commands and queries.' },
|
|
17
|
+
{ label: 'module', insertText: 'module ${1:Name}\n ', documentation: 'Declares the top-level namespace — maps to a bounded context.' },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export const moduleItems: CompletionEntry[] = [
|
|
21
|
+
{ label: 'layout', insertText: 'layout ${1:Name}\n template\n ${2:slot}', documentation: 'Declares a reusable screen template with named slots.' },
|
|
22
|
+
{ label: 'feature', insertText: 'feature ${1:Name}\n ', documentation: 'Groups related slices into a vertical feature.' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const featureItems: CompletionEntry[] = [
|
|
26
|
+
{ label: 'feature', insertText: 'feature ${1:Name}\n ', documentation: 'Declares a nested sub-feature.' },
|
|
27
|
+
{ label: 'slice StateChange', insertText: 'slice StateChange ${1:Name}\n ', documentation: 'A command → events flow; something that changes the system.' },
|
|
28
|
+
{ label: 'slice StateView', insertText: 'slice StateView ${1:Name}\n ', documentation: 'A query + projection + screen; something that reads the system.' },
|
|
29
|
+
{ label: 'slice Automation', insertText: 'slice Automation ${1:Name}\n ', documentation: 'A reactor or reducer; something that reacts to events.' },
|
|
30
|
+
{ label: 'slice Translate', insertText: 'slice Translate ${1:Name}\n ', documentation: 'A capture; converts external data into events.' },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export const sliceItems: CompletionEntry[] = [
|
|
34
|
+
{ label: 'event', insertText: 'event ${1:Name}\n ${2:property} ${3:Type}', documentation: 'Declares an event type — an immutable, past-tense fact.' },
|
|
35
|
+
{ label: 'command', insertText: 'command ${1:Name}\n ${2:property} ${3:Type}', documentation: 'Declares a command — an imperative intent that produces events.' },
|
|
36
|
+
{ label: 'query', insertText: 'query ${1:Name} => ${2:ReadModel}', documentation: 'Declares a read-side entry point mapping to a return type.' },
|
|
37
|
+
{ label: 'projection', insertText: 'projection ${1:Name} => ${2:ReadModel}\n from ${3:EventType}', documentation: 'Declares how events project into a read model (PDL).' },
|
|
38
|
+
{ label: 'capture', insertText: 'capture ${1:Name}\n source ${2:api}', documentation: 'Declares a change data capture converting external data into events (CDL).' },
|
|
39
|
+
{ label: 'reactor', insertText: 'reactor ${1:Name}\n on ${2:EventType}', documentation: 'Declares an event reaction rule.' },
|
|
40
|
+
{ label: 'screen', insertText: 'screen ${1:Name}\n data ${2:ReadModel} via query ${3:QueryName}', documentation: 'Declares a UI screen.' },
|
|
41
|
+
{ label: 'constraint', insertText: 'constraint ${1:Name}\n unique ${2:property} on ${3:EventType}', documentation: 'Declares a server-side rule enforced before events are committed.' },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export const commandItems: CompletionEntry[] = [
|
|
45
|
+
{ label: 'authorize', insertText: 'authorize ${1:PolicyName}', documentation: 'References the policies that must pass for the command to execute.' },
|
|
46
|
+
{ label: 'validate', insertText: 'validate\n ${1:property} not empty message "${2:message}"', documentation: 'Declarative validation rules with messages.' },
|
|
47
|
+
{ label: 'validate csharp', insertText: `validate ${fenced('csharp')}`, documentation: 'Imperative validation in C#, yielding ValidationError.' },
|
|
48
|
+
{ label: 'produces', insertText: 'produces ${1:EventType}\n ${2:property} = ${3:source}', documentation: 'Declares the event the command emits, with property mappings.' },
|
|
49
|
+
{ label: 'produces when', insertText: 'produces when ${1:condition}\n ${2:EventType}\n ${3:property} = ${4:source}', documentation: 'Conditionally emits an event when the condition holds.' },
|
|
50
|
+
{ label: 'handler', insertText: 'handler\n ', documentation: 'Fully imperative command implementation — file reference or inline C#, instead of produces.' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export const producesItems: CompletionEntry[] = [
|
|
54
|
+
{ label: 'when', insertText: 'when ${1:condition}', documentation: 'Guards the produced event with a condition.' },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
export const handlerItems: CompletionEntry[] = [
|
|
58
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Delegates the command implementation to an external C# file.' },
|
|
59
|
+
{ label: 'csharp', insertText: fenced('csharp'), documentation: 'Inline C# returning the events to append.' },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
export const queryItems: CompletionEntry[] = [
|
|
63
|
+
{ label: 'by', insertText: 'by ${1:param} ${2:Type}', documentation: 'Declares the identifying parameter of the query.' },
|
|
64
|
+
{ label: 'filter', insertText: 'filter ${1:param} ${2:Type}?', documentation: 'Declares an optional filter parameter.' },
|
|
65
|
+
{ label: 'authorize', insertText: 'authorize ${1:PolicyName}', documentation: 'References the policies that must pass for the query to execute.' },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
export const constraintItems: CompletionEntry[] = [
|
|
69
|
+
{ label: 'unique', insertText: 'unique ${1:property} on ${2:EventType}', documentation: 'Enforces a unique property value across an event type.' },
|
|
70
|
+
{ label: 'unique event', insertText: 'unique event ${1:EventType}', documentation: 'Enforces that the event type occurs at most once per event source.' },
|
|
71
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Delegates the constraint to a custom C# implementation.' },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
export const reactorItems: CompletionEntry[] = [
|
|
75
|
+
{ label: 'on', insertText: 'on ${1:EventType}', documentation: 'Declares the event the reactor reacts to.' },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
export const reactorOnItems: CompletionEntry[] = [
|
|
79
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Delegates the reaction to an external C# file.' },
|
|
80
|
+
{ label: 'csharp', insertText: fenced('csharp'), documentation: 'Inline C# returning event side effects.' },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export const policyItems: CompletionEntry[] = [
|
|
84
|
+
{ label: 'require authenticated', insertText: 'require authenticated', documentation: 'Requires an authenticated caller.' },
|
|
85
|
+
{ label: 'require role', insertText: 'require role "${1:role}"', documentation: 'Requires the caller to have a role.' },
|
|
86
|
+
{ label: 'require claim', insertText: 'require claim "${1:claim}" matches ${2:subject}', documentation: 'Requires a claim to match the subject or a value.' },
|
|
87
|
+
{ label: 'csharp', insertText: fenced('csharp'), documentation: 'Fully custom policy logic in C#, returning PolicyResult.' },
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
export const validateItems: CompletionEntry[] = [
|
|
91
|
+
{ label: 'not empty', insertText: '${1:property} not empty message "${2:message}"', documentation: 'The property must have a value.' },
|
|
92
|
+
{ label: 'max', insertText: '${1:property} max ${2:500} message "${3:message}"', documentation: 'Maximum length or value.' },
|
|
93
|
+
{ label: 'min', insertText: '${1:property} min ${2:1} message "${3:message}"', documentation: 'Minimum length or value.' },
|
|
94
|
+
{ label: 'matches', insertText: '${1:property} matches "${2:pattern}" message "${3:message}"', documentation: 'The property must match a regular expression.' },
|
|
95
|
+
{ label: 'length ==', insertText: '${1:property} length == ${2:3} message "${3:message}"', documentation: 'The property must have an exact length.' },
|
|
96
|
+
{ label: 'all >', insertText: '${1:collection}.${2:property} all > ${3:0} message "${4:message}"', documentation: 'Every element of a collection must satisfy the comparison.' },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
export const screenItems: CompletionEntry[] = [
|
|
100
|
+
{ label: 'data', insertText: 'data ${1:ReadModel} via query ${2:QueryName}', documentation: 'Binds a read model to the screen through a query.' },
|
|
101
|
+
{ label: 'action', insertText: 'action ${1:CommandName}', documentation: 'Makes a command available as an action on the screen.' },
|
|
102
|
+
{ label: 'layout', insertText: 'layout ${1:LayoutName}', documentation: 'Uses a layout template and fills its slots.' },
|
|
103
|
+
{ label: 'section', insertText: 'section ${1:name}', documentation: 'A named structural section of the screen.' },
|
|
104
|
+
{ label: 'table', insertText: 'table ${1:name}\n column ${2:property} label "${3:text}"', documentation: 'A table widget over a read model or collection.' },
|
|
105
|
+
{ label: 'summary', insertText: 'summary ${1:ReadModel}\n field ${2:property} label "${3:text}"', documentation: 'A summary widget showing labeled fields.' },
|
|
106
|
+
{ label: 'title', insertText: 'title "${1:text}"', documentation: 'The title of the screen or section.' },
|
|
107
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Full external implementation — Stage uses the referenced file.' },
|
|
108
|
+
{ label: 'react', insertText: fenced('react'), documentation: 'Inline React/TSX component receiving the data contract as Props.' },
|
|
109
|
+
{ label: 'typescript', insertText: fenced('typescript'), documentation: 'Inline plain TypeScript.' },
|
|
110
|
+
{ label: 'html', insertText: fenced('html'), documentation: 'Inline static HTML.' },
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
export const actionItems: CompletionEntry[] = [
|
|
114
|
+
{ label: 'navigate to', insertText: 'navigate to ${1:ScreenName}', documentation: 'Navigates to a screen after the action completes.' },
|
|
115
|
+
{ label: 'label', insertText: 'label "${1:text}"', documentation: 'The display label of the action.' },
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
export const tableItems: CompletionEntry[] = [
|
|
119
|
+
{ label: 'column', insertText: 'column ${1:property} label "${2:text}"', documentation: 'A column bound to a property.' },
|
|
120
|
+
{ label: 'on row-click', insertText: 'on row-click navigate to ${1:ScreenName} by ${2:param}', documentation: 'Navigates when a row is clicked.' },
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
export const contextVariableItems: CompletionEntry[] = [
|
|
124
|
+
{ label: '$context.occurred', insertText: '$context.occurred', documentation: 'Timestamp of the event.' },
|
|
125
|
+
{ label: '$context.identity.id', insertText: '$context.identity.id', documentation: 'Subject of the caller from the auth token.' },
|
|
126
|
+
{ label: '$env.', insertText: '$env.${1:VAR_NAME}', documentation: 'An environment variable.' },
|
|
127
|
+
{ label: '$eventContext.occurred', insertText: '$eventContext.occurred', documentation: 'Timestamp of the event being projected (PDL).' },
|
|
128
|
+
];
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { enclosingChain, fenceMap, indentOf } from './document-context';
|
|
5
|
+
import { getSubLanguage } from './sub-language-registry';
|
|
6
|
+
import * as items from './completion-items';
|
|
7
|
+
import { CompletionEntry } from './completion-items';
|
|
8
|
+
|
|
9
|
+
export type CompletionPlan =
|
|
10
|
+
| { kind: 'none' }
|
|
11
|
+
| { kind: 'entries'; entries: CompletionEntry[] }
|
|
12
|
+
| { kind: 'contextVariables'; replaceLength: number }
|
|
13
|
+
| { kind: 'policies' }
|
|
14
|
+
| { kind: 'events' }
|
|
15
|
+
| { kind: 'producesTargets' }
|
|
16
|
+
| { kind: 'screens' }
|
|
17
|
+
| { kind: 'queries' }
|
|
18
|
+
| { kind: 'types' };
|
|
19
|
+
|
|
20
|
+
export function completionEntriesFor(chain: string[]): CompletionEntry[] {
|
|
21
|
+
const construct = chain[0];
|
|
22
|
+
if (construct === undefined) return items.topLevelItems;
|
|
23
|
+
const subLanguage = getSubLanguage(construct);
|
|
24
|
+
if (subLanguage) return subLanguage.completions ?? [];
|
|
25
|
+
switch (construct) {
|
|
26
|
+
case 'module':
|
|
27
|
+
return items.moduleItems;
|
|
28
|
+
case 'feature':
|
|
29
|
+
return items.featureItems;
|
|
30
|
+
case 'slice':
|
|
31
|
+
return items.sliceItems;
|
|
32
|
+
case 'command':
|
|
33
|
+
return items.commandItems;
|
|
34
|
+
case 'produces':
|
|
35
|
+
return items.producesItems;
|
|
36
|
+
case 'handler':
|
|
37
|
+
return items.handlerItems;
|
|
38
|
+
case 'query':
|
|
39
|
+
return items.queryItems;
|
|
40
|
+
case 'constraint':
|
|
41
|
+
return items.constraintItems;
|
|
42
|
+
case 'reactor':
|
|
43
|
+
return items.reactorItems;
|
|
44
|
+
case 'on':
|
|
45
|
+
return items.reactorOnItems;
|
|
46
|
+
case 'policy':
|
|
47
|
+
return items.policyItems;
|
|
48
|
+
case 'validate':
|
|
49
|
+
return items.validateItems;
|
|
50
|
+
case 'action':
|
|
51
|
+
return items.actionItems;
|
|
52
|
+
case 'table':
|
|
53
|
+
case 'summary':
|
|
54
|
+
return items.tableItems;
|
|
55
|
+
default:
|
|
56
|
+
// Layout slots and sections inside a screen expose the screen vocabulary.
|
|
57
|
+
if (chain.includes('screen') || construct === 'section' || construct === 'layout') {
|
|
58
|
+
return items.screenItems;
|
|
59
|
+
}
|
|
60
|
+
if (chain.includes('produces')) return items.producesItems;
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Decides what to complete at a position, without any editor dependency — the
|
|
66
|
+
// Monaco service and the VSCode extension both materialize the plan into items.
|
|
67
|
+
export function planCompletions(
|
|
68
|
+
lines: string[],
|
|
69
|
+
lineIndex: number,
|
|
70
|
+
textBefore: string,
|
|
71
|
+
): CompletionPlan {
|
|
72
|
+
const fences = fenceMap(lines);
|
|
73
|
+
if (fences[lineIndex]) return { kind: 'none' };
|
|
74
|
+
|
|
75
|
+
const contextVariableMatch = textBefore.match(/\$[\w.]*$/);
|
|
76
|
+
if (contextVariableMatch) {
|
|
77
|
+
return { kind: 'contextVariables', replaceLength: contextVariableMatch[0].length };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const currentLine = lines[lineIndex] ?? '';
|
|
81
|
+
const effectiveIndent =
|
|
82
|
+
textBefore.trim().length === 0 ? textBefore.length : indentOf(currentLine);
|
|
83
|
+
const chain = enclosingChain(lines, fences, lineIndex, effectiveIndent);
|
|
84
|
+
|
|
85
|
+
if (/\bauthorize\s+[\w\s]*$/.test(textBefore) || chain[0] === 'authorize') {
|
|
86
|
+
return { kind: 'policies' };
|
|
87
|
+
}
|
|
88
|
+
if (/\bon\s+\w*$/.test(textBefore) && (chain.includes('reactor') || chain.includes('constraint'))) {
|
|
89
|
+
return { kind: 'events' };
|
|
90
|
+
}
|
|
91
|
+
if (/\b(?:on|unique\s+event)\s+\w*$/.test(textBefore) && chain[0] === 'constraint') {
|
|
92
|
+
return { kind: 'events' };
|
|
93
|
+
}
|
|
94
|
+
if (/\bproduces\s+\w*$/.test(textBefore)) {
|
|
95
|
+
return { kind: 'producesTargets' };
|
|
96
|
+
}
|
|
97
|
+
if (/\bnavigate\s+to\s+\w*$/.test(textBefore)) {
|
|
98
|
+
return { kind: 'screens' };
|
|
99
|
+
}
|
|
100
|
+
if (/\bvia\s+query\s+\w*$/.test(textBefore)) {
|
|
101
|
+
return { kind: 'queries' };
|
|
102
|
+
}
|
|
103
|
+
if (
|
|
104
|
+
(chain[0] === 'event' || chain[0] === 'command') &&
|
|
105
|
+
/^\s+[a-z_]\w*\s+[\w[\]?]*$/.test(textBefore)
|
|
106
|
+
) {
|
|
107
|
+
return { kind: 'types' };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { kind: 'entries', entries: completionEntriesFor(chain) };
|
|
111
|
+
}
|
package/completions.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { languages } from 'monaco-editor';
|
|
5
|
+
import { Monaco, primitiveTypes } from './language';
|
|
6
|
+
import { knownEventNames, scanDocument } from './symbols';
|
|
7
|
+
import { planCompletions } from './completion-planner';
|
|
8
|
+
import { contextVariableItems, producesItems, CompletionEntry } from './completion-items';
|
|
9
|
+
|
|
10
|
+
export function createCompletionProvider(monaco: Monaco): languages.CompletionItemProvider {
|
|
11
|
+
return {
|
|
12
|
+
triggerCharacters: [' ', '$', '@', '.'],
|
|
13
|
+
|
|
14
|
+
provideCompletionItems(model, position) {
|
|
15
|
+
const lines = model.getLinesContent();
|
|
16
|
+
const lineIndex = position.lineNumber - 1;
|
|
17
|
+
const currentLine = lines[lineIndex] ?? '';
|
|
18
|
+
const textBefore = currentLine.substring(0, position.column - 1);
|
|
19
|
+
const plan = planCompletions(lines, lineIndex, textBefore);
|
|
20
|
+
if (plan.kind === 'none') return { suggestions: [] };
|
|
21
|
+
|
|
22
|
+
const symbols = scanDocument(lines);
|
|
23
|
+
const word = model.getWordUntilPosition(position);
|
|
24
|
+
const range = new monaco.Range(
|
|
25
|
+
position.lineNumber,
|
|
26
|
+
word.startColumn,
|
|
27
|
+
position.lineNumber,
|
|
28
|
+
word.endColumn,
|
|
29
|
+
);
|
|
30
|
+
const kinds = monaco.languages.CompletionItemKind;
|
|
31
|
+
const asSnippet = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
|
32
|
+
|
|
33
|
+
const snippet = (entry: CompletionEntry): languages.CompletionItem => ({
|
|
34
|
+
label: entry.label,
|
|
35
|
+
kind: entry.insertText.includes('$') ? kinds.Snippet : kinds.Keyword,
|
|
36
|
+
insertText: entry.insertText,
|
|
37
|
+
insertTextRules: asSnippet,
|
|
38
|
+
documentation: entry.documentation,
|
|
39
|
+
range,
|
|
40
|
+
});
|
|
41
|
+
const symbolItem = (
|
|
42
|
+
name: string,
|
|
43
|
+
kind: languages.CompletionItemKind,
|
|
44
|
+
detail: string,
|
|
45
|
+
): languages.CompletionItem => ({
|
|
46
|
+
label: name,
|
|
47
|
+
kind,
|
|
48
|
+
insertText: name,
|
|
49
|
+
detail,
|
|
50
|
+
range,
|
|
51
|
+
});
|
|
52
|
+
const eventNames = () =>
|
|
53
|
+
knownEventNames(symbols).map((name) => symbolItem(name, kinds.Event, 'event'));
|
|
54
|
+
|
|
55
|
+
switch (plan.kind) {
|
|
56
|
+
case 'contextVariables': {
|
|
57
|
+
const variableRange = new monaco.Range(
|
|
58
|
+
position.lineNumber,
|
|
59
|
+
position.column - plan.replaceLength,
|
|
60
|
+
position.lineNumber,
|
|
61
|
+
word.endColumn,
|
|
62
|
+
);
|
|
63
|
+
return {
|
|
64
|
+
suggestions: contextVariableItems.map((entry) => ({
|
|
65
|
+
...snippet(entry),
|
|
66
|
+
range: variableRange,
|
|
67
|
+
})),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
case 'policies':
|
|
71
|
+
return {
|
|
72
|
+
suggestions: symbols.policies.map((policy) =>
|
|
73
|
+
symbolItem(policy.name, kinds.Reference, 'policy'),
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
case 'events':
|
|
77
|
+
return { suggestions: eventNames() };
|
|
78
|
+
case 'producesTargets':
|
|
79
|
+
return { suggestions: [...producesItems.map(snippet), ...eventNames()] };
|
|
80
|
+
case 'screens':
|
|
81
|
+
return {
|
|
82
|
+
suggestions: symbols.screens.map((screen) =>
|
|
83
|
+
symbolItem(screen.name, kinds.Interface, 'screen'),
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
case 'queries':
|
|
87
|
+
return {
|
|
88
|
+
suggestions: symbols.queries.map((query) =>
|
|
89
|
+
symbolItem(query.name, kinds.Function, `query => ${query.returnType}`),
|
|
90
|
+
),
|
|
91
|
+
};
|
|
92
|
+
case 'types':
|
|
93
|
+
return {
|
|
94
|
+
suggestions: [
|
|
95
|
+
...symbols.concepts.map((concept) =>
|
|
96
|
+
symbolItem(
|
|
97
|
+
concept.name,
|
|
98
|
+
kinds.Class,
|
|
99
|
+
`concept : ${concept.primitive}${concept.attributes.length ? ' ' + concept.attributes.join(' ') : ''}`,
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
...primitiveTypes.map((primitive) =>
|
|
103
|
+
symbolItem(primitive, kinds.Struct, 'primitive'),
|
|
104
|
+
),
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
case 'entries':
|
|
108
|
+
return { suggestions: plan.entries.map(snippet) };
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
package/diagnostics.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import type { editor } from 'monaco-editor';
|
|
5
|
+
import { Monaco, languageId } from './language';
|
|
6
|
+
import { validateLines } from './validation';
|
|
7
|
+
|
|
8
|
+
const owner = 'screenplay';
|
|
9
|
+
const validationDelay = 300;
|
|
10
|
+
|
|
11
|
+
export function validate(monaco: Monaco, model: editor.ITextModel): editor.IMarkerData[] {
|
|
12
|
+
return validateLines(model.getLinesContent()).map((issue) => ({
|
|
13
|
+
severity:
|
|
14
|
+
issue.severity === 'error'
|
|
15
|
+
? monaco.MarkerSeverity.Error
|
|
16
|
+
: monaco.MarkerSeverity.Warning,
|
|
17
|
+
message: issue.message,
|
|
18
|
+
startLineNumber: issue.line + 1,
|
|
19
|
+
startColumn: issue.startColumn,
|
|
20
|
+
endLineNumber: issue.line + 1,
|
|
21
|
+
endColumn: issue.endColumn,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function watch(monaco: Monaco, model: editor.ITextModel): void {
|
|
26
|
+
let handle: ReturnType<typeof setTimeout> | undefined;
|
|
27
|
+
|
|
28
|
+
const run = () => {
|
|
29
|
+
if (model.isDisposed()) return;
|
|
30
|
+
if (model.getLanguageId() !== languageId) {
|
|
31
|
+
monaco.editor.setModelMarkers(model, owner, []);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
monaco.editor.setModelMarkers(model, owner, validate(monaco, model));
|
|
35
|
+
};
|
|
36
|
+
const schedule = () => {
|
|
37
|
+
if (handle !== undefined) clearTimeout(handle);
|
|
38
|
+
handle = setTimeout(run, validationDelay);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
model.onDidChangeContent(schedule);
|
|
42
|
+
model.onDidChangeLanguage(run);
|
|
43
|
+
model.onWillDispose(() => {
|
|
44
|
+
if (handle !== undefined) clearTimeout(handle);
|
|
45
|
+
});
|
|
46
|
+
run();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function attachDiagnostics(monaco: Monaco): void {
|
|
50
|
+
monaco.editor.getModels().forEach((model) => watch(monaco, model));
|
|
51
|
+
monaco.editor.onDidCreateModel((model) => watch(monaco, model));
|
|
52
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface CompletionEntry {
|
|
2
|
+
label: string;
|
|
3
|
+
insertText: string;
|
|
4
|
+
documentation: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const topLevelItems: CompletionEntry[];
|
|
7
|
+
export declare const moduleItems: CompletionEntry[];
|
|
8
|
+
export declare const featureItems: CompletionEntry[];
|
|
9
|
+
export declare const sliceItems: CompletionEntry[];
|
|
10
|
+
export declare const commandItems: CompletionEntry[];
|
|
11
|
+
export declare const producesItems: CompletionEntry[];
|
|
12
|
+
export declare const handlerItems: CompletionEntry[];
|
|
13
|
+
export declare const queryItems: CompletionEntry[];
|
|
14
|
+
export declare const constraintItems: CompletionEntry[];
|
|
15
|
+
export declare const reactorItems: CompletionEntry[];
|
|
16
|
+
export declare const reactorOnItems: CompletionEntry[];
|
|
17
|
+
export declare const policyItems: CompletionEntry[];
|
|
18
|
+
export declare const validateItems: CompletionEntry[];
|
|
19
|
+
export declare const screenItems: CompletionEntry[];
|
|
20
|
+
export declare const actionItems: CompletionEntry[];
|
|
21
|
+
export declare const tableItems: CompletionEntry[];
|
|
22
|
+
export declare const contextVariableItems: CompletionEntry[];
|
|
23
|
+
//# sourceMappingURL=completion-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-items.d.ts","sourceRoot":"","sources":["../completion-items.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACzB;AAID,eAAO,MAAM,aAAa,EAAE,eAAe,EAM1C,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAAe,EAGxC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAAe,EAMzC,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,eAAe,EASvC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAAe,EAOzC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,eAAe,EAE1C,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAAe,EAGzC,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,eAAe,EAIvC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAAe,EAI5C,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAAe,EAEzC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,eAAe,EAG3C,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAAe,EAKxC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,eAAe,EAO1C,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAAe,EAYxC,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAAe,EAGxC,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,eAAe,EAGvC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAAe,EAKjD,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
const fenced = (tag) => `${tag}\n\`\`\`\n\${1}\n\`\`\``;
|
|
4
|
+
export const topLevelItems = [
|
|
5
|
+
{ label: 'import', insertText: 'import ${1:Module}.${2:Type}', documentation: 'Imports a type from another module by its qualified name.' },
|
|
6
|
+
{ label: 'concept', insertText: 'concept ${1:Name} : ${2|Uuid,String,Int,Decimal,Bool,Date,DateTime|}', documentation: 'Declares a formalized value type wrapping a primitive.' },
|
|
7
|
+
{ label: 'concept (enum)', insertText: 'concept ${1:Name} : Enum\n ${2:value}', documentation: 'Declares an enumeration concept with a fixed set of values.' },
|
|
8
|
+
{ label: 'policy', insertText: 'policy ${1:Name}\n require ${2:authenticated}', documentation: 'Declares a named authorization rule for commands and queries.' },
|
|
9
|
+
{ label: 'module', insertText: 'module ${1:Name}\n ', documentation: 'Declares the top-level namespace — maps to a bounded context.' },
|
|
10
|
+
];
|
|
11
|
+
export const moduleItems = [
|
|
12
|
+
{ label: 'layout', insertText: 'layout ${1:Name}\n template\n ${2:slot}', documentation: 'Declares a reusable screen template with named slots.' },
|
|
13
|
+
{ label: 'feature', insertText: 'feature ${1:Name}\n ', documentation: 'Groups related slices into a vertical feature.' },
|
|
14
|
+
];
|
|
15
|
+
export const featureItems = [
|
|
16
|
+
{ label: 'feature', insertText: 'feature ${1:Name}\n ', documentation: 'Declares a nested sub-feature.' },
|
|
17
|
+
{ label: 'slice StateChange', insertText: 'slice StateChange ${1:Name}\n ', documentation: 'A command → events flow; something that changes the system.' },
|
|
18
|
+
{ label: 'slice StateView', insertText: 'slice StateView ${1:Name}\n ', documentation: 'A query + projection + screen; something that reads the system.' },
|
|
19
|
+
{ label: 'slice Automation', insertText: 'slice Automation ${1:Name}\n ', documentation: 'A reactor or reducer; something that reacts to events.' },
|
|
20
|
+
{ label: 'slice Translate', insertText: 'slice Translate ${1:Name}\n ', documentation: 'A capture; converts external data into events.' },
|
|
21
|
+
];
|
|
22
|
+
export const sliceItems = [
|
|
23
|
+
{ label: 'event', insertText: 'event ${1:Name}\n ${2:property} ${3:Type}', documentation: 'Declares an event type — an immutable, past-tense fact.' },
|
|
24
|
+
{ label: 'command', insertText: 'command ${1:Name}\n ${2:property} ${3:Type}', documentation: 'Declares a command — an imperative intent that produces events.' },
|
|
25
|
+
{ label: 'query', insertText: 'query ${1:Name} => ${2:ReadModel}', documentation: 'Declares a read-side entry point mapping to a return type.' },
|
|
26
|
+
{ label: 'projection', insertText: 'projection ${1:Name} => ${2:ReadModel}\n from ${3:EventType}', documentation: 'Declares how events project into a read model (PDL).' },
|
|
27
|
+
{ label: 'capture', insertText: 'capture ${1:Name}\n source ${2:api}', documentation: 'Declares a change data capture converting external data into events (CDL).' },
|
|
28
|
+
{ label: 'reactor', insertText: 'reactor ${1:Name}\n on ${2:EventType}', documentation: 'Declares an event reaction rule.' },
|
|
29
|
+
{ label: 'screen', insertText: 'screen ${1:Name}\n data ${2:ReadModel} via query ${3:QueryName}', documentation: 'Declares a UI screen.' },
|
|
30
|
+
{ label: 'constraint', insertText: 'constraint ${1:Name}\n unique ${2:property} on ${3:EventType}', documentation: 'Declares a server-side rule enforced before events are committed.' },
|
|
31
|
+
];
|
|
32
|
+
export const commandItems = [
|
|
33
|
+
{ label: 'authorize', insertText: 'authorize ${1:PolicyName}', documentation: 'References the policies that must pass for the command to execute.' },
|
|
34
|
+
{ label: 'validate', insertText: 'validate\n ${1:property} not empty message "${2:message}"', documentation: 'Declarative validation rules with messages.' },
|
|
35
|
+
{ label: 'validate csharp', insertText: `validate ${fenced('csharp')}`, documentation: 'Imperative validation in C#, yielding ValidationError.' },
|
|
36
|
+
{ label: 'produces', insertText: 'produces ${1:EventType}\n ${2:property} = ${3:source}', documentation: 'Declares the event the command emits, with property mappings.' },
|
|
37
|
+
{ label: 'produces when', insertText: 'produces when ${1:condition}\n ${2:EventType}\n ${3:property} = ${4:source}', documentation: 'Conditionally emits an event when the condition holds.' },
|
|
38
|
+
{ label: 'handler', insertText: 'handler\n ', documentation: 'Fully imperative command implementation — file reference or inline C#, instead of produces.' },
|
|
39
|
+
];
|
|
40
|
+
export const producesItems = [
|
|
41
|
+
{ label: 'when', insertText: 'when ${1:condition}', documentation: 'Guards the produced event with a condition.' },
|
|
42
|
+
];
|
|
43
|
+
export const handlerItems = [
|
|
44
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Delegates the command implementation to an external C# file.' },
|
|
45
|
+
{ label: 'csharp', insertText: fenced('csharp'), documentation: 'Inline C# returning the events to append.' },
|
|
46
|
+
];
|
|
47
|
+
export const queryItems = [
|
|
48
|
+
{ label: 'by', insertText: 'by ${1:param} ${2:Type}', documentation: 'Declares the identifying parameter of the query.' },
|
|
49
|
+
{ label: 'filter', insertText: 'filter ${1:param} ${2:Type}?', documentation: 'Declares an optional filter parameter.' },
|
|
50
|
+
{ label: 'authorize', insertText: 'authorize ${1:PolicyName}', documentation: 'References the policies that must pass for the query to execute.' },
|
|
51
|
+
];
|
|
52
|
+
export const constraintItems = [
|
|
53
|
+
{ label: 'unique', insertText: 'unique ${1:property} on ${2:EventType}', documentation: 'Enforces a unique property value across an event type.' },
|
|
54
|
+
{ label: 'unique event', insertText: 'unique event ${1:EventType}', documentation: 'Enforces that the event type occurs at most once per event source.' },
|
|
55
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Delegates the constraint to a custom C# implementation.' },
|
|
56
|
+
];
|
|
57
|
+
export const reactorItems = [
|
|
58
|
+
{ label: 'on', insertText: 'on ${1:EventType}', documentation: 'Declares the event the reactor reacts to.' },
|
|
59
|
+
];
|
|
60
|
+
export const reactorOnItems = [
|
|
61
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Delegates the reaction to an external C# file.' },
|
|
62
|
+
{ label: 'csharp', insertText: fenced('csharp'), documentation: 'Inline C# returning event side effects.' },
|
|
63
|
+
];
|
|
64
|
+
export const policyItems = [
|
|
65
|
+
{ label: 'require authenticated', insertText: 'require authenticated', documentation: 'Requires an authenticated caller.' },
|
|
66
|
+
{ label: 'require role', insertText: 'require role "${1:role}"', documentation: 'Requires the caller to have a role.' },
|
|
67
|
+
{ label: 'require claim', insertText: 'require claim "${1:claim}" matches ${2:subject}', documentation: 'Requires a claim to match the subject or a value.' },
|
|
68
|
+
{ label: 'csharp', insertText: fenced('csharp'), documentation: 'Fully custom policy logic in C#, returning PolicyResult.' },
|
|
69
|
+
];
|
|
70
|
+
export const validateItems = [
|
|
71
|
+
{ label: 'not empty', insertText: '${1:property} not empty message "${2:message}"', documentation: 'The property must have a value.' },
|
|
72
|
+
{ label: 'max', insertText: '${1:property} max ${2:500} message "${3:message}"', documentation: 'Maximum length or value.' },
|
|
73
|
+
{ label: 'min', insertText: '${1:property} min ${2:1} message "${3:message}"', documentation: 'Minimum length or value.' },
|
|
74
|
+
{ label: 'matches', insertText: '${1:property} matches "${2:pattern}" message "${3:message}"', documentation: 'The property must match a regular expression.' },
|
|
75
|
+
{ label: 'length ==', insertText: '${1:property} length == ${2:3} message "${3:message}"', documentation: 'The property must have an exact length.' },
|
|
76
|
+
{ label: 'all >', insertText: '${1:collection}.${2:property} all > ${3:0} message "${4:message}"', documentation: 'Every element of a collection must satisfy the comparison.' },
|
|
77
|
+
];
|
|
78
|
+
export const screenItems = [
|
|
79
|
+
{ label: 'data', insertText: 'data ${1:ReadModel} via query ${2:QueryName}', documentation: 'Binds a read model to the screen through a query.' },
|
|
80
|
+
{ label: 'action', insertText: 'action ${1:CommandName}', documentation: 'Makes a command available as an action on the screen.' },
|
|
81
|
+
{ label: 'layout', insertText: 'layout ${1:LayoutName}', documentation: 'Uses a layout template and fills its slots.' },
|
|
82
|
+
{ label: 'section', insertText: 'section ${1:name}', documentation: 'A named structural section of the screen.' },
|
|
83
|
+
{ label: 'table', insertText: 'table ${1:name}\n column ${2:property} label "${3:text}"', documentation: 'A table widget over a read model or collection.' },
|
|
84
|
+
{ label: 'summary', insertText: 'summary ${1:ReadModel}\n field ${2:property} label "${3:text}"', documentation: 'A summary widget showing labeled fields.' },
|
|
85
|
+
{ label: 'title', insertText: 'title "${1:text}"', documentation: 'The title of the screen or section.' },
|
|
86
|
+
{ label: 'file', insertText: 'file ${1:Path}', documentation: 'Full external implementation — Stage uses the referenced file.' },
|
|
87
|
+
{ label: 'react', insertText: fenced('react'), documentation: 'Inline React/TSX component receiving the data contract as Props.' },
|
|
88
|
+
{ label: 'typescript', insertText: fenced('typescript'), documentation: 'Inline plain TypeScript.' },
|
|
89
|
+
{ label: 'html', insertText: fenced('html'), documentation: 'Inline static HTML.' },
|
|
90
|
+
];
|
|
91
|
+
export const actionItems = [
|
|
92
|
+
{ label: 'navigate to', insertText: 'navigate to ${1:ScreenName}', documentation: 'Navigates to a screen after the action completes.' },
|
|
93
|
+
{ label: 'label', insertText: 'label "${1:text}"', documentation: 'The display label of the action.' },
|
|
94
|
+
];
|
|
95
|
+
export const tableItems = [
|
|
96
|
+
{ label: 'column', insertText: 'column ${1:property} label "${2:text}"', documentation: 'A column bound to a property.' },
|
|
97
|
+
{ label: 'on row-click', insertText: 'on row-click navigate to ${1:ScreenName} by ${2:param}', documentation: 'Navigates when a row is clicked.' },
|
|
98
|
+
];
|
|
99
|
+
export const contextVariableItems = [
|
|
100
|
+
{ label: '$context.occurred', insertText: '$context.occurred', documentation: 'Timestamp of the event.' },
|
|
101
|
+
{ label: '$context.identity.id', insertText: '$context.identity.id', documentation: 'Subject of the caller from the auth token.' },
|
|
102
|
+
{ label: '$env.', insertText: '$env.${1:VAR_NAME}', documentation: 'An environment variable.' },
|
|
103
|
+
{ label: '$eventContext.occurred', insertText: '$eventContext.occurred', documentation: 'Timestamp of the event being projected (PDL).' },
|
|
104
|
+
];
|
|
105
|
+
//# sourceMappingURL=completion-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-items.js","sourceRoot":"","sources":["../completion-items.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CompletionEntry } from './completion-items';
|
|
2
|
+
export type CompletionPlan = {
|
|
3
|
+
kind: 'none';
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'entries';
|
|
6
|
+
entries: CompletionEntry[];
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'contextVariables';
|
|
9
|
+
replaceLength: number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'policies';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'events';
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'producesTargets';
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'screens';
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'queries';
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'types';
|
|
22
|
+
};
|
|
23
|
+
export declare function completionEntriesFor(chain: string[]): CompletionEntry[];
|
|
24
|
+
export declare function planCompletions(lines: string[], lineIndex: number, textBefore: string): CompletionPlan;
|
|
25
|
+
//# sourceMappingURL=completion-planner.d.ts.map
|