@cratis/pi 2.24.1 → 2.26.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/package/corpus/prompts/ship-changes.prompt.md +7 -0
- package/package/corpus/rules/java.md +134 -0
- package/package/corpus/rules/kotlin.md +172 -0
- package/package/corpus/rules/profiles.md +24 -3
- package/package/corpus/rules/pull-requests.md +25 -0
- package/package/corpus/skills/cratis-arc-command-kotlin/SKILL.md +367 -0
- package/package/corpus/skills/cratis-arc-query-kotlin/SKILL.md +225 -0
- package/package/corpus/skills/cratis-arc-validation-kotlin/SKILL.md +285 -0
- package/package/profile-catalog.json +81 -8
- package/package.json +1 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-arc-validation-kotlin
|
|
3
|
+
description: Add a rejection rule to a Cratis Arc command or query in Kotlin or Java — FluentModelValidator shared rules that also emit TypeScript, plus the server-only CommandValidator, QueryValidator, ConceptValidator, and ModelValidator. Use when a Kotlin or Java Arc command or query must refuse work under some condition. Do not use to define a new command or query, and do not use for append-time Chronicle constraints.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Validate an Arc command or query in Kotlin or Java
|
|
8
|
+
|
|
9
|
+
Arc.Kotlin has two validation surfaces, and they compose rather than compete:
|
|
10
|
+
**shared fluent rules** that a build step also turns into generated TypeScript
|
|
11
|
+
client-side checks, and **ordinary imperative validators** that run only on
|
|
12
|
+
the server. Pick shared rules first for anything literal enough to express
|
|
13
|
+
that way; keep service calls, async work, and cross-member logic imperative.
|
|
14
|
+
|
|
15
|
+
## Verified product sources
|
|
16
|
+
|
|
17
|
+
Verified against `Arc.Kotlin` at tag **`v7.3.0`**, re-verified at commit
|
|
18
|
+
`d0aa6a4` (14 commits past the tag; `guides/commands.md`, `guides/validation.md`,
|
|
19
|
+
and `guides/chronicle.md` are byte-for-byte unchanged since the tag).
|
|
20
|
+
Documented consumer setup: JDK 17, Kotlin 2.4.20, KSP 2.3.12, Spring Boot
|
|
21
|
+
4.1.x. `io.cratis:arc` supplies `io.cratis.arc.validation.FluentModelValidator`,
|
|
22
|
+
`CommandValidator<T>`, `QueryValidator<T>`, `ConceptValidator<T>`,
|
|
23
|
+
`ModelValidator<T>`, `ConceptValidationExclusion`, `IgnoreValidation`,
|
|
24
|
+
`CommandFilter`, `QueryFilter`. The re-verification added the new
|
|
25
|
+
"Reject at feature scope with a pipeline filter" section below, from the new
|
|
26
|
+
`guides/pipeline-filters.md`.
|
|
27
|
+
|
|
28
|
+
## Shared fluent rules — server and generated TypeScript
|
|
29
|
+
|
|
30
|
+
Use `FluentModelValidator<T>` for a literal member rule that must run
|
|
31
|
+
identically on the server and in the generated client. It is a public, final,
|
|
32
|
+
top-level class over one concrete model, with one public no-argument
|
|
33
|
+
constructor and a single `init` block containing only direct `ruleFor(...)`
|
|
34
|
+
chains with literal arguments — KSP statically compiles this shape, so no
|
|
35
|
+
lambda, computed accessor, or helper method is allowed inside it.
|
|
36
|
+
|
|
37
|
+
```kotlin
|
|
38
|
+
package example.tasks
|
|
39
|
+
|
|
40
|
+
import io.cratis.arc.validation.FluentModelValidator
|
|
41
|
+
|
|
42
|
+
class CreateTaskRules : FluentModelValidator<CreateTask>(CreateTask::class.java) {
|
|
43
|
+
init {
|
|
44
|
+
ruleFor("title").notEmpty().withMessage("{PropertyName} must have text; {PropertyName}")
|
|
45
|
+
ruleFor("title").maxLength(120)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```java
|
|
51
|
+
package example.tasks;
|
|
52
|
+
|
|
53
|
+
import io.cratis.arc.validation.FluentModelValidator;
|
|
54
|
+
|
|
55
|
+
public final class CreateTaskRules extends FluentModelValidator<CreateTask> {
|
|
56
|
+
public CreateTaskRules() {
|
|
57
|
+
super(CreateTask.class);
|
|
58
|
+
ruleFor("title").notEmpty();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Java uses the ordinary constructor and method calls — no Kotlin lambda or
|
|
64
|
+
`Continuation`. Records with default or source-proved identity accessors and
|
|
65
|
+
public fields are supported.
|
|
66
|
+
|
|
67
|
+
### The rule vocabulary
|
|
68
|
+
|
|
69
|
+
| Rule | Checks |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `notNull()` | value is not `null` |
|
|
72
|
+
| `notEmpty()` | non-blank string |
|
|
73
|
+
| `minLength(n)` / `maxLength(n)` / `length(min, max)` | string length |
|
|
74
|
+
| `emailAddress()` | email shape |
|
|
75
|
+
| `phone()` | digits/whitespace/parens/plus/hyphen only |
|
|
76
|
+
| `url()` | `http://`/`https://` prefix |
|
|
77
|
+
| `matches(regex)` | regex match |
|
|
78
|
+
| `greaterThan(n)` / `greaterThanOrEqual(n)` | numeric lower bound |
|
|
79
|
+
| `lessThan(n)` / `lessThanOrEqual(n)` | numeric upper bound |
|
|
80
|
+
| `withMessage(text)` | overrides the preceding rule's message; replaces only the first `{PropertyName}` token |
|
|
81
|
+
|
|
82
|
+
There is no shared `creditCard()` fluent rule — use the server-only
|
|
83
|
+
`@CreditCard` annotation instead (below); the pinned `@cratis/arc` client has
|
|
84
|
+
no matching client-side check for it.
|
|
85
|
+
|
|
86
|
+
Select **one direct public readable member** per `ruleFor(...)` — not a dotted
|
|
87
|
+
path, not an index. Shared rules apply to present nested nodes and collection
|
|
88
|
+
siblings automatically: a `null` parent creates no model node, so a child's
|
|
89
|
+
`notNull()` does not implicitly require the parent to be present — add a
|
|
90
|
+
presence rule on the parent member when the parent itself is required.
|
|
91
|
+
|
|
92
|
+
### Wire it up
|
|
93
|
+
|
|
94
|
+
Register nothing by hand. The generated module registers every discovered
|
|
95
|
+
`FluentModelValidator` automatically:
|
|
96
|
+
|
|
97
|
+
```kotlin
|
|
98
|
+
cratisArc {
|
|
99
|
+
moduleName.set("<ModuleName>")
|
|
100
|
+
dependencyVersion.set("<version>")
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The Arc Gradle plugin wires the dependency index shared validation needs
|
|
105
|
+
automatically. A manual KSP build must extract and pass that index itself —
|
|
106
|
+
see the Arc.Kotlin `Documentation/reference/configuration.md` dependency
|
|
107
|
+
extraction recipe before reaching for manual wiring.
|
|
108
|
+
|
|
109
|
+
### Opt a member out
|
|
110
|
+
|
|
111
|
+
```kotlin
|
|
112
|
+
import io.cratis.arc.validation.IgnoreValidation
|
|
113
|
+
|
|
114
|
+
data class CreateTask(
|
|
115
|
+
@IgnoreValidation val internalNote: String?,
|
|
116
|
+
val title: String
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`@IgnoreValidation` cuts one logical validation edge while keeping
|
|
121
|
+
serialization and binding intact — it is not `@JsonIgnore`. Kotlin accepts it
|
|
122
|
+
on the property, `@field:`, or `@get:` placement; Java accepts it on a field,
|
|
123
|
+
bean getter, or record component. Nonignored siblings and independently
|
|
124
|
+
validated child roots still run.
|
|
125
|
+
|
|
126
|
+
## Server-only validators
|
|
127
|
+
|
|
128
|
+
### `CommandValidator<T>` / `QueryValidator<T>`
|
|
129
|
+
|
|
130
|
+
```kotlin
|
|
131
|
+
@Component
|
|
132
|
+
class CreateTaskValidator : CommandValidator<CreateTask> {
|
|
133
|
+
override val commandType = CreateTask::class.java
|
|
134
|
+
|
|
135
|
+
override suspend fun validate(command: CreateTask, context: CommandContext): List<ValidationResult> =
|
|
136
|
+
if (command.title.isBlank()) {
|
|
137
|
+
listOf(ValidationResult.error("A task title is required.", listOf("title")))
|
|
138
|
+
} else {
|
|
139
|
+
emptyList()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Register a Spring bean; `POST <command-route>/validate` runs the same
|
|
145
|
+
pipeline without invoking `handle`. `ValidationResult.information` /
|
|
146
|
+
`.warning` / `.error` are static factories naming severity, members, state,
|
|
147
|
+
reason, and reason detail — Java reads them the same way. Without an explicit
|
|
148
|
+
threshold, only errors block execution; add `@TreatWarningsAsErrors` to make
|
|
149
|
+
`Information` the nonblocking ceiling, so warnings and errors both block.
|
|
150
|
+
Callers can override per request with the `X-Allowed-Severity` header.
|
|
151
|
+
|
|
152
|
+
Java implementations use `BlockingCommandValidator`/`AsyncCommandValidator`
|
|
153
|
+
with the matching `*Adapter`, published as an ordinary `CommandValidator`
|
|
154
|
+
Spring bean — there is no second Java-specific discovery mechanism.
|
|
155
|
+
|
|
156
|
+
### `ConceptValidator<TConcept>`
|
|
157
|
+
|
|
158
|
+
A reusable invariant on a `ConceptAs<T>` concept type, walked automatically
|
|
159
|
+
wherever that concept type appears — command, nested model, collection,
|
|
160
|
+
array, or map — without each owner repeating the rule. Matching uses
|
|
161
|
+
`conceptType.isInstance`, including subtypes; failures attach to the owning
|
|
162
|
+
member's path.
|
|
163
|
+
|
|
164
|
+
```kotlin
|
|
165
|
+
@Component
|
|
166
|
+
class TaskTitleValidator : ConceptValidator<TaskTitle> {
|
|
167
|
+
override val conceptType = TaskTitle::class.java
|
|
168
|
+
|
|
169
|
+
override fun validate(concept: TaskTitle): List<ValidationResult> =
|
|
170
|
+
if (concept.value.isBlank()) listOf(ValidationResult.error("A task title is required.")) else emptyList()
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Concept rules run independently of Jakarta `@Valid` and do not require a
|
|
175
|
+
Jakarta `Validator` bean. Exclude one direct edge deliberately with
|
|
176
|
+
`ConceptValidationExclusion(ownerType, member)` — the owner must match the
|
|
177
|
+
**exact runtime class**, and the member must be one direct public property (or
|
|
178
|
+
Java record component/field) whose *declared* type implements `ConceptAs`. A
|
|
179
|
+
scalar root, a collection element, or a nested path cannot be excluded this
|
|
180
|
+
way — register the nested owner type and its direct member instead. Excluding
|
|
181
|
+
an edge suppresses only `ConceptValidator` execution there; owner rules, exact
|
|
182
|
+
`ModelValidator` rules, Jakarta constraints, and root typed validators still
|
|
183
|
+
run.
|
|
184
|
+
|
|
185
|
+
### `ModelValidator<T>`
|
|
186
|
+
|
|
187
|
+
A server-only rule reused by command roots, nested models, and supplied query
|
|
188
|
+
arguments, matched by **exact runtime class** (not subclasses or interfaces).
|
|
189
|
+
Nodes run parent-before-child, model rules before concept rules; records use
|
|
190
|
+
declaration order, Kotlin public properties use alphabetical order. Java
|
|
191
|
+
implementations use `BlockingModelValidator`/`AsyncModelValidator` with the
|
|
192
|
+
matching adapter, published as a `ModelValidator` bean.
|
|
193
|
+
|
|
194
|
+
### Jakarta constraints
|
|
195
|
+
|
|
196
|
+
When Spring has a Jakarta `Validator` bean (add `spring-boot-starter-validation`
|
|
197
|
+
for one), the starter installs a command/query filter automatically. It
|
|
198
|
+
validates `@Valid`-marked nested objects, arrays, iterables, and maps; maps a
|
|
199
|
+
path like `items[0].name` into Arc member paths; and terminates safely on
|
|
200
|
+
cyclic graphs. `@Phone`, `@Url`, `@CreditCard` are Jakarta constraints that
|
|
201
|
+
work from both Kotlin and Java; `@Phone`/`@Url` also emit matching generated
|
|
202
|
+
TypeScript rules, while `@CreditCard` stays server-only because the pinned
|
|
203
|
+
client runtime has no compatible check. `null` is always valid for these three
|
|
204
|
+
— pair with `@NotNull` when absence itself is invalid.
|
|
205
|
+
|
|
206
|
+
## Reject at feature scope with a pipeline filter
|
|
207
|
+
|
|
208
|
+
`CommandValidator`/`ConceptValidator`/`ModelValidator` all attach a rule to a
|
|
209
|
+
specific artifact or type. When a rule instead belongs to a whole *feature* —
|
|
210
|
+
"everything under the billing package needs the billing role" — writing it on
|
|
211
|
+
every command in turn means the next command somebody adds is the one that
|
|
212
|
+
forgets it. A `CommandFilter`/`QueryFilter` is that declaration, written once:
|
|
213
|
+
|
|
214
|
+
```kotlin
|
|
215
|
+
class BillingCommandFilter : CommandFilter {
|
|
216
|
+
override suspend fun execute(context: CommandContext): CommandResult<*> {
|
|
217
|
+
if (!context.commandType.name.startsWith(BILLING_PACKAGE)) {
|
|
218
|
+
return CommandResult.success(context.correlationId)
|
|
219
|
+
}
|
|
220
|
+
return if (context.principal.isInRole(BILLING_ROLE)) {
|
|
221
|
+
CommandResult.success(context.correlationId)
|
|
222
|
+
} else {
|
|
223
|
+
CommandResult.unauthorized(context.correlationId, "Role '$BILLING_ROLE' is required.")
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@Bean
|
|
229
|
+
fun billingCommandFilter(): CommandFilter = BillingCommandFilter()
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- A `QueryFilter` takes the same shape against `QueryContext`, matched on the
|
|
233
|
+
query's fully qualified name; it runs for an observable query too, at
|
|
234
|
+
subscription time, so one rule covers both the one-shot `GET` and the live
|
|
235
|
+
subscription.
|
|
236
|
+
- A filter that does not apply to the artifact in front of it must return
|
|
237
|
+
success and get out of the way — that first check is what keeps a feature's
|
|
238
|
+
rule scoped to the feature instead of becoming a global one by accident.
|
|
239
|
+
- Java implements `BlockingCommandFilter`/`BlockingQueryFilter` (synchronous)
|
|
240
|
+
or `AsyncCommandFilter`/`AsyncQueryFilter` (`CompletionStage`), registered
|
|
241
|
+
through the matching `*FilterAdapter`.
|
|
242
|
+
- Filters run in Spring `@Order` sequence — that order is the complete rule,
|
|
243
|
+
with one exception: a filter implementing `AuthorizationCommandFilter` /
|
|
244
|
+
`AuthorizationQueryFilter` always runs before the rest, regardless of its
|
|
245
|
+
own `@Order`, so a denial is never reached after a filter that already
|
|
246
|
+
caused an effect.
|
|
247
|
+
- Reach for a filter only when the rule genuinely spans artifacts. A rule
|
|
248
|
+
about one command or query reads closer to the code it governs, and stays
|
|
249
|
+
visible in the generated client and OpenAPI document, as `@Authorize`/
|
|
250
|
+
`@Roles` or one of the validators above — prefer those first.
|
|
251
|
+
|
|
252
|
+
## Order of execution
|
|
253
|
+
|
|
254
|
+
Typed `CommandValidator`/`QueryValidator` rules run once, before the shared
|
|
255
|
+
model/concept traversal. A separately registered `ModelValidator` for the
|
|
256
|
+
command's own class is a distinct rule that also runs once at the root.
|
|
257
|
+
Cancellation from any validator — a concept validator, a Kotlin property
|
|
258
|
+
getter, a Java record accessor — propagates through execution and marks a
|
|
259
|
+
cancelled nested command's root rollback-only. An ordinary thrown exception
|
|
260
|
+
from a validator becomes safe `validatorFailed` feedback instead, not an
|
|
261
|
+
exception result; it is not silently skipped.
|
|
262
|
+
|
|
263
|
+
## Route near misses
|
|
264
|
+
|
|
265
|
+
- Defining the command or query the rule attaches to:
|
|
266
|
+
`cratis-arc-command-kotlin`, `cratis-arc-query-kotlin`.
|
|
267
|
+
- Append-time Chronicle uniqueness/concurrency: `cratis-chronicle-event-constraints`.
|
|
268
|
+
- The C# Arc validation shape (`ConceptValidator<T>`, `CommandValidator<T>`,
|
|
269
|
+
Provide short-circuiting): `cratis-arc-command-validation`.
|
|
270
|
+
|
|
271
|
+
## Verify
|
|
272
|
+
|
|
273
|
+
- Every `FluentModelValidator` is public, final, top-level, has exactly one
|
|
274
|
+
public no-argument constructor, and its `init` block contains only direct
|
|
275
|
+
`ruleFor(...)` chains with literal arguments.
|
|
276
|
+
- A rejection the user can act on comes back as a `ValidationResult`, never a
|
|
277
|
+
thrown exception.
|
|
278
|
+
- A concept rule that must not run on one specific edge is excluded through
|
|
279
|
+
`ConceptValidationExclusion`, not by weakening the concept's own rule.
|
|
280
|
+
- `@IgnoreValidation` is used only to cut validation, never in place of
|
|
281
|
+
`@JsonIgnore` for a value that must not serialize at all.
|
|
282
|
+
- The generated TypeScript client rejects the same input the server does for
|
|
283
|
+
every rule expressed with `FluentModelValidator` — verify with a build that
|
|
284
|
+
regenerates the proxy.
|
|
285
|
+
- `./gradlew build` is clean with zero warnings and zero errors.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"cratis/application/chronicle-dotnet",
|
|
11
11
|
"cratis/application/csharp",
|
|
12
12
|
"cratis/application/elixir",
|
|
13
|
+
"cratis/application/java",
|
|
13
14
|
"cratis/application/kotlin",
|
|
14
15
|
"cratis/application/react",
|
|
15
16
|
"cratis/application/typescript",
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
"cratis/arc/csharp",
|
|
20
21
|
"cratis/arc/ef-core",
|
|
21
22
|
"cratis/arc/identity",
|
|
23
|
+
"cratis/arc/java",
|
|
22
24
|
"cratis/arc/kotlin",
|
|
23
25
|
"cratis/arc/react",
|
|
24
26
|
"cratis/chronicle",
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
"cratis/chronicle/core",
|
|
33
35
|
"cratis/chronicle/csharp",
|
|
34
36
|
"cratis/chronicle/elixir",
|
|
37
|
+
"cratis/chronicle/java",
|
|
35
38
|
"cratis/chronicle/kotlin",
|
|
36
39
|
"cratis/chronicle/mcp",
|
|
37
40
|
"cratis/chronicle/multi-tenancy",
|
|
@@ -44,12 +47,14 @@
|
|
|
44
47
|
"cratis/full",
|
|
45
48
|
"cratis/full/csharp",
|
|
46
49
|
"cratis/full/elixir",
|
|
50
|
+
"cratis/full/java",
|
|
47
51
|
"cratis/full/kotlin",
|
|
48
52
|
"cratis/full/typescript",
|
|
49
53
|
"cratis/fundamentals",
|
|
50
54
|
"cratis/fundamentals/type-discovery",
|
|
51
55
|
"cratis/language/csharp",
|
|
52
56
|
"cratis/language/elixir",
|
|
57
|
+
"cratis/language/java",
|
|
53
58
|
"cratis/language/kotlin",
|
|
54
59
|
"cratis/language/typescript",
|
|
55
60
|
"cratis/lens",
|
|
@@ -80,6 +85,7 @@
|
|
|
80
85
|
"composes": [
|
|
81
86
|
"cratis/application/csharp",
|
|
82
87
|
"cratis/application/elixir",
|
|
88
|
+
"cratis/application/java",
|
|
83
89
|
"cratis/application/kotlin",
|
|
84
90
|
"cratis/application/typescript",
|
|
85
91
|
"cratis/arc/core",
|
|
@@ -93,6 +99,7 @@
|
|
|
93
99
|
"languages": [
|
|
94
100
|
"csharp",
|
|
95
101
|
"elixir",
|
|
102
|
+
"java",
|
|
96
103
|
"kotlin",
|
|
97
104
|
"react",
|
|
98
105
|
"typescript"
|
|
@@ -161,6 +168,17 @@
|
|
|
161
168
|
"elixir"
|
|
162
169
|
]
|
|
163
170
|
},
|
|
171
|
+
{
|
|
172
|
+
"id": "cratis/application/java",
|
|
173
|
+
"composes": [
|
|
174
|
+
"cratis/arc/client-kotlin",
|
|
175
|
+
"cratis/chronicle/client-java",
|
|
176
|
+
"cratis/language/java"
|
|
177
|
+
],
|
|
178
|
+
"languages": [
|
|
179
|
+
"java"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
164
182
|
{
|
|
165
183
|
"id": "cratis/application/kotlin",
|
|
166
184
|
"composes": [
|
|
@@ -204,18 +222,26 @@
|
|
|
204
222
|
"id": "cratis/arc",
|
|
205
223
|
"composes": [
|
|
206
224
|
"cratis/arc/csharp",
|
|
225
|
+
"cratis/arc/java",
|
|
207
226
|
"cratis/arc/kotlin"
|
|
208
227
|
],
|
|
209
228
|
"languages": [
|
|
210
229
|
"csharp",
|
|
230
|
+
"java",
|
|
231
|
+
"kotlin",
|
|
211
232
|
"react",
|
|
212
|
-
"typescript"
|
|
213
|
-
"kotlin"
|
|
233
|
+
"typescript"
|
|
214
234
|
]
|
|
215
235
|
},
|
|
216
236
|
{
|
|
217
237
|
"id": "cratis/arc/client-kotlin",
|
|
238
|
+
"availableTargets": [
|
|
239
|
+
"cratis-arc-command-kotlin",
|
|
240
|
+
"cratis-arc-query-kotlin",
|
|
241
|
+
"cratis-arc-validation-kotlin"
|
|
242
|
+
],
|
|
218
243
|
"languages": [
|
|
244
|
+
"java",
|
|
219
245
|
"kotlin"
|
|
220
246
|
]
|
|
221
247
|
},
|
|
@@ -274,6 +300,16 @@
|
|
|
274
300
|
"typescript"
|
|
275
301
|
]
|
|
276
302
|
},
|
|
303
|
+
{
|
|
304
|
+
"id": "cratis/arc/java",
|
|
305
|
+
"composes": [
|
|
306
|
+
"cratis/arc/client-kotlin",
|
|
307
|
+
"cratis/language/java"
|
|
308
|
+
],
|
|
309
|
+
"languages": [
|
|
310
|
+
"java"
|
|
311
|
+
]
|
|
312
|
+
},
|
|
277
313
|
{
|
|
278
314
|
"id": "cratis/arc/kotlin",
|
|
279
315
|
"composes": [
|
|
@@ -304,6 +340,7 @@
|
|
|
304
340
|
"cratis/chronicle/compliance",
|
|
305
341
|
"cratis/chronicle/csharp",
|
|
306
342
|
"cratis/chronicle/elixir",
|
|
343
|
+
"cratis/chronicle/java",
|
|
307
344
|
"cratis/chronicle/kotlin",
|
|
308
345
|
"cratis/chronicle/multi-tenancy",
|
|
309
346
|
"cratis/chronicle/typescript",
|
|
@@ -311,10 +348,11 @@
|
|
|
311
348
|
],
|
|
312
349
|
"languages": [
|
|
313
350
|
"csharp",
|
|
314
|
-
"kotlin",
|
|
315
351
|
"elixir",
|
|
316
|
-
"
|
|
317
|
-
"
|
|
352
|
+
"java",
|
|
353
|
+
"kotlin",
|
|
354
|
+
"language-agnostic",
|
|
355
|
+
"typescript"
|
|
318
356
|
]
|
|
319
357
|
},
|
|
320
358
|
{
|
|
@@ -337,6 +375,9 @@
|
|
|
337
375
|
},
|
|
338
376
|
{
|
|
339
377
|
"id": "cratis/chronicle/client-java",
|
|
378
|
+
"composes": [
|
|
379
|
+
"cratis/chronicle/client-kotlin"
|
|
380
|
+
],
|
|
340
381
|
"languages": [
|
|
341
382
|
"java"
|
|
342
383
|
]
|
|
@@ -347,6 +388,7 @@
|
|
|
347
388
|
"cratis-chronicle-client-kotlin"
|
|
348
389
|
],
|
|
349
390
|
"languages": [
|
|
391
|
+
"java",
|
|
350
392
|
"kotlin"
|
|
351
393
|
]
|
|
352
394
|
},
|
|
@@ -418,6 +460,16 @@
|
|
|
418
460
|
"elixir"
|
|
419
461
|
]
|
|
420
462
|
},
|
|
463
|
+
{
|
|
464
|
+
"id": "cratis/chronicle/java",
|
|
465
|
+
"composes": [
|
|
466
|
+
"cratis/chronicle/client-java",
|
|
467
|
+
"cratis/language/java"
|
|
468
|
+
],
|
|
469
|
+
"languages": [
|
|
470
|
+
"java"
|
|
471
|
+
]
|
|
472
|
+
},
|
|
421
473
|
{
|
|
422
474
|
"id": "cratis/chronicle/kotlin",
|
|
423
475
|
"composes": [
|
|
@@ -522,16 +574,18 @@
|
|
|
522
574
|
"composes": [
|
|
523
575
|
"cratis/full/csharp",
|
|
524
576
|
"cratis/full/elixir",
|
|
577
|
+
"cratis/full/java",
|
|
525
578
|
"cratis/full/kotlin",
|
|
526
579
|
"cratis/full/typescript"
|
|
527
580
|
],
|
|
528
581
|
"languages": [
|
|
529
582
|
"csharp",
|
|
530
|
-
"kotlin",
|
|
531
583
|
"elixir",
|
|
532
|
-
"
|
|
584
|
+
"java",
|
|
585
|
+
"kotlin",
|
|
586
|
+
"language-agnostic",
|
|
533
587
|
"react",
|
|
534
|
-
"
|
|
588
|
+
"typescript"
|
|
535
589
|
]
|
|
536
590
|
},
|
|
537
591
|
{
|
|
@@ -561,6 +615,19 @@
|
|
|
561
615
|
"language-agnostic"
|
|
562
616
|
]
|
|
563
617
|
},
|
|
618
|
+
{
|
|
619
|
+
"id": "cratis/full/java",
|
|
620
|
+
"composes": [
|
|
621
|
+
"cratis/application/java",
|
|
622
|
+
"cratis/arc/java",
|
|
623
|
+
"cratis/chronicle/java",
|
|
624
|
+
"cratis/modeling/screenplay-stage"
|
|
625
|
+
],
|
|
626
|
+
"languages": [
|
|
627
|
+
"java",
|
|
628
|
+
"language-agnostic"
|
|
629
|
+
]
|
|
630
|
+
},
|
|
564
631
|
{
|
|
565
632
|
"id": "cratis/full/kotlin",
|
|
566
633
|
"composes": [
|
|
@@ -620,6 +687,12 @@
|
|
|
620
687
|
"elixir"
|
|
621
688
|
]
|
|
622
689
|
},
|
|
690
|
+
{
|
|
691
|
+
"id": "cratis/language/java",
|
|
692
|
+
"languages": [
|
|
693
|
+
"java"
|
|
694
|
+
]
|
|
695
|
+
},
|
|
623
696
|
{
|
|
624
697
|
"id": "cratis/language/kotlin",
|
|
625
698
|
"languages": [
|