sirius-framework-mcp 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/LICENSE +191 -0
- package/README.md +160 -0
- package/dist/grammars/tree-sitter-java.wasm +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +247 -0
- package/dist/index.js.map +1 -0
- package/dist/java-parser.d.ts +25 -0
- package/dist/java-parser.js +281 -0
- package/dist/java-parser.js.map +1 -0
- package/dist/prompts/index.d.ts +9 -0
- package/dist/prompts/index.js +15 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/workflows.d.ts +27 -0
- package/dist/prompts/workflows.js +317 -0
- package/dist/prompts/workflows.js.map +1 -0
- package/dist/resources/biz/analytics.md +157 -0
- package/dist/resources/biz/biz-controller.md +151 -0
- package/dist/resources/biz/codelists.md +154 -0
- package/dist/resources/biz/entity-triple.md +142 -0
- package/dist/resources/biz/importer.md +153 -0
- package/dist/resources/biz/isenguard.md +156 -0
- package/dist/resources/biz/jobs.md +145 -0
- package/dist/resources/biz/processes.md +155 -0
- package/dist/resources/biz/storage.md +149 -0
- package/dist/resources/biz/tenants.md +159 -0
- package/dist/resources/biz/testing.md +127 -0
- package/dist/resources/db/composites.md +145 -0
- package/dist/resources/db/entities.md +156 -0
- package/dist/resources/db/mixing.md +176 -0
- package/dist/resources/db/queries.md +178 -0
- package/dist/resources/db/refs.md +135 -0
- package/dist/resources/index.d.ts +27 -0
- package/dist/resources/index.js +68 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/kernel/async.md +189 -0
- package/dist/resources/kernel/commons.md +203 -0
- package/dist/resources/kernel/config.md +155 -0
- package/dist/resources/kernel/di.md +138 -0
- package/dist/resources/kernel/lifecycle.md +146 -0
- package/dist/resources/loader.d.ts +9 -0
- package/dist/resources/loader.js +17 -0
- package/dist/resources/loader.js.map +1 -0
- package/dist/resources/web/controllers.md +151 -0
- package/dist/resources/web/services.md +136 -0
- package/dist/resources/web/templates.md +162 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspection.d.ts +55 -0
- package/dist/tools/introspection.js +233 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/tools/scaffold.d.ts +64 -0
- package/dist/tools/scaffold.js +505 -0
- package/dist/tools/scaffold.js.map +1 -0
- package/dist/workspace.d.ts +37 -0
- package/dist/workspace.js +185 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Queries
|
|
2
|
+
|
|
3
|
+
Sirius-db provides a consistent query builder pattern across all three database
|
|
4
|
+
backends. Each mapper has its own `select()` method that returns a typed query
|
|
5
|
+
object, but the API surface is deliberately similar.
|
|
6
|
+
|
|
7
|
+
## OMA — JDBC Queries (SmartQuery)
|
|
8
|
+
|
|
9
|
+
`OMA` is the mapper for `SQLEntity`. Inject it with `@Part`:
|
|
10
|
+
|
|
11
|
+
```java
|
|
12
|
+
@Part
|
|
13
|
+
private OMA oma;
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Select (query builder):**
|
|
17
|
+
```java
|
|
18
|
+
SmartQuery<Product> query = oma.select(Product.class)
|
|
19
|
+
.eq(Product.CATEGORY, "electronics")
|
|
20
|
+
.orderAsc(Product.NAME)
|
|
21
|
+
.limit(50);
|
|
22
|
+
|
|
23
|
+
List<Product> products = query.queryList();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Find by ID:**
|
|
27
|
+
```java
|
|
28
|
+
Optional<Product> product = oma.find(Product.class, productId);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Count:**
|
|
32
|
+
```java
|
|
33
|
+
long count = oma.select(Product.class)
|
|
34
|
+
.eq(Product.ACTIVE, true)
|
|
35
|
+
.count();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Exists check:**
|
|
39
|
+
```java
|
|
40
|
+
boolean exists = oma.select(Product.class)
|
|
41
|
+
.eq(Product.SKU, sku)
|
|
42
|
+
.exists();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Iteration (streaming):**
|
|
46
|
+
```java
|
|
47
|
+
oma.select(Product.class)
|
|
48
|
+
.eq(Product.ACTIVE, true)
|
|
49
|
+
.iterateAll(product -> {
|
|
50
|
+
// process each product
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Update and delete:**
|
|
55
|
+
```java
|
|
56
|
+
oma.update(product);
|
|
57
|
+
oma.delete(product);
|
|
58
|
+
oma.forceDelete(product); // bypasses REJECT constraints
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Mango — MongoDB Queries (MongoQuery)
|
|
62
|
+
|
|
63
|
+
`Mango` is the mapper for `MongoEntity`:
|
|
64
|
+
|
|
65
|
+
```java
|
|
66
|
+
@Part
|
|
67
|
+
private Mango mango;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Select:**
|
|
71
|
+
```java
|
|
72
|
+
MongoQuery<MongoProduct> query = mango.select(MongoProduct.class)
|
|
73
|
+
.eq(MongoProduct.CATEGORY, "electronics")
|
|
74
|
+
.orderAsc(MongoProduct.NAME)
|
|
75
|
+
.limit(50);
|
|
76
|
+
|
|
77
|
+
List<MongoProduct> products = query.queryList();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Find by ID:**
|
|
81
|
+
```java
|
|
82
|
+
Optional<MongoProduct> product = mango.find(MongoProduct.class, productId);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The query API mirrors OMA: `count()`, `exists()`, `iterateAll()`, `queryList()`,
|
|
86
|
+
`queryFirst()`, `delete()`, and `update()` all work the same way.
|
|
87
|
+
|
|
88
|
+
## Elastic — Elasticsearch Queries (ElasticQuery)
|
|
89
|
+
|
|
90
|
+
`Elastic` is the mapper for `ElasticEntity`:
|
|
91
|
+
|
|
92
|
+
```java
|
|
93
|
+
@Part
|
|
94
|
+
private Elastic elastic;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Select:**
|
|
98
|
+
```java
|
|
99
|
+
ElasticQuery<EventLog> query = elastic.select(EventLog.class)
|
|
100
|
+
.eq(EventLog.EVENT_TYPE, "login")
|
|
101
|
+
.orderDesc(EventLog.TIMESTAMP)
|
|
102
|
+
.limit(100);
|
|
103
|
+
|
|
104
|
+
List<EventLog> events = query.queryList();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Find by ID:**
|
|
108
|
+
```java
|
|
109
|
+
Optional<EventLog> event = elastic.find(EventLog.class, eventId);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Elastic queries also support full-text search and aggregations, but the basic
|
|
113
|
+
filter/sort/limit API is the same as OMA and Mango.
|
|
114
|
+
|
|
115
|
+
## Common Query Methods
|
|
116
|
+
|
|
117
|
+
All three query types share these methods from the `Query` base class:
|
|
118
|
+
|
|
119
|
+
| Method | Description |
|
|
120
|
+
|--------|-------------|
|
|
121
|
+
| `eq(Mapping, value)` | Equals filter |
|
|
122
|
+
| `ne(Mapping, value)` | Not-equals filter |
|
|
123
|
+
| `gt(Mapping, value)` | Greater than |
|
|
124
|
+
| `gte(Mapping, value)` | Greater than or equal |
|
|
125
|
+
| `lt(Mapping, value)` | Less than |
|
|
126
|
+
| `lte(Mapping, value)` | Less than or equal |
|
|
127
|
+
| `orderAsc(Mapping)` | Sort ascending |
|
|
128
|
+
| `orderDesc(Mapping)` | Sort descending |
|
|
129
|
+
| `limit(int)` | Maximum results |
|
|
130
|
+
| `skip(int)` | Skip n results |
|
|
131
|
+
| `queryList()` | Return all matching as list |
|
|
132
|
+
| `queryFirst()` | Return first match as Optional |
|
|
133
|
+
| `count()` | Return count of matches |
|
|
134
|
+
| `exists()` | Return true if any match exists |
|
|
135
|
+
| `iterateAll(Consumer)` | Stream all matches through a consumer |
|
|
136
|
+
| `delete()` | Delete all matching entities |
|
|
137
|
+
|
|
138
|
+
## Filter Factories
|
|
139
|
+
|
|
140
|
+
Each mapper provides a `FILTERS` factory for complex constraints:
|
|
141
|
+
|
|
142
|
+
```java
|
|
143
|
+
oma.select(Product.class)
|
|
144
|
+
.where(OMA.FILTERS.or(
|
|
145
|
+
OMA.FILTERS.eq(Product.CATEGORY, "electronics"),
|
|
146
|
+
OMA.FILTERS.eq(Product.CATEGORY, "appliances")
|
|
147
|
+
))
|
|
148
|
+
.queryList();
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Mango and Elastic have equivalent `FILTERS` factories with the same API.
|
|
152
|
+
|
|
153
|
+
## Querying Composite Fields
|
|
154
|
+
|
|
155
|
+
Use `Mapping.inner()` to query fields inside composites:
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
oma.select(Customer.class)
|
|
159
|
+
.eq(Customer.PERSON.inner(PersonData.LASTNAME), "Smith")
|
|
160
|
+
.queryList();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Common Mistakes
|
|
164
|
+
|
|
165
|
+
1. **Calling queryList() on unbounded queries** — Always set a `limit()` or use
|
|
166
|
+
`iterateAll()` for large result sets. An unbounded `queryList()` loads
|
|
167
|
+
everything into memory.
|
|
168
|
+
|
|
169
|
+
2. **Using raw strings instead of Mapping constants** — Always use `Mapping.named()`
|
|
170
|
+
constants. Raw strings bypass compile-time safety and break on refactoring.
|
|
171
|
+
|
|
172
|
+
3. **Mixing mapper types** — Use `oma` for `SQLEntity`, `mango` for `MongoEntity`,
|
|
173
|
+
and `elastic` for `ElasticEntity`. Each entity class is bound to exactly one
|
|
174
|
+
mapper.
|
|
175
|
+
|
|
176
|
+
4. **Ignoring iterateAll cancellation** — `iterateAll()` respects `TaskContext`
|
|
177
|
+
cancellation. Long-running iterations should check `TaskContext.get().isActive()`
|
|
178
|
+
or will be automatically stopped when the task is cancelled.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Entity References
|
|
2
|
+
|
|
3
|
+
Entity references model relationships between entities in sirius-db. Instead of
|
|
4
|
+
storing a raw foreign key, references are wrapped in typed ref objects that provide
|
|
5
|
+
lazy loading, delete behavior, and type safety.
|
|
6
|
+
|
|
7
|
+
## BaseEntityRef<I, E> — Common Interface
|
|
8
|
+
|
|
9
|
+
All reference types extend `BaseEntityRef<I, E>` (in `sirius.db.mixing.types`),
|
|
10
|
+
where `I` is the ID type and `E` is the entity type. It stores the referenced
|
|
11
|
+
entity's ID and lazily loads the full entity on demand.
|
|
12
|
+
|
|
13
|
+
Key methods on any ref:
|
|
14
|
+
- `getId()` — returns the stored ID (without loading the entity)
|
|
15
|
+
- `getValue()` — loads and returns the referenced entity (cached after first load)
|
|
16
|
+
- `getValueIfPresent()` — returns the entity only if already loaded
|
|
17
|
+
- `setId(I)` — sets the reference by ID
|
|
18
|
+
- `setValue(E)` — sets the reference by entity instance
|
|
19
|
+
- `isEmpty()` / `isFilled()` — checks whether the reference is set
|
|
20
|
+
- `is(E)` — checks if the ref points to the given entity
|
|
21
|
+
- `hasWriteOnceSemantics()` — whether the ref is immutable after first save
|
|
22
|
+
|
|
23
|
+
## OnDelete Behaviors
|
|
24
|
+
|
|
25
|
+
The `OnDelete` enum defines what happens when the referenced entity is deleted:
|
|
26
|
+
|
|
27
|
+
- `CASCADE` — the entity holding the reference is also deleted
|
|
28
|
+
- `SET_NULL` — the reference field is set to null
|
|
29
|
+
- `REJECT` — the delete is rejected (throws an exception)
|
|
30
|
+
- `IGNORE` — no action is taken; the reference may become dangling
|
|
31
|
+
|
|
32
|
+
## SQLEntityRef<E>
|
|
33
|
+
|
|
34
|
+
For SQL entities, use `SQLEntityRef<E>`. The ID type is `Long`.
|
|
35
|
+
|
|
36
|
+
```java
|
|
37
|
+
public class OrderItem extends SQLTenantAware {
|
|
38
|
+
|
|
39
|
+
public static final Mapping ORDER = Mapping.named("order");
|
|
40
|
+
private final SQLEntityRef<Order> order =
|
|
41
|
+
SQLEntityRef.on(Order.class, BaseEntityRef.OnDelete.CASCADE);
|
|
42
|
+
|
|
43
|
+
public static final Mapping PRODUCT = Mapping.named("product");
|
|
44
|
+
private final SQLEntityRef<Product> product =
|
|
45
|
+
SQLEntityRef.on(Product.class, BaseEntityRef.OnDelete.REJECT);
|
|
46
|
+
|
|
47
|
+
// getters...
|
|
48
|
+
public SQLEntityRef<Order> getOrder() { return order; }
|
|
49
|
+
public SQLEntityRef<Product> getProduct() { return product; }
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### on() vs writeOnceOn()
|
|
54
|
+
|
|
55
|
+
- `SQLEntityRef.on(Class, OnDelete)` — creates a **mutable** reference. The value
|
|
56
|
+
can be changed at any time.
|
|
57
|
+
- `SQLEntityRef.writeOnceOn(Class, OnDelete)` — creates an **immutable** reference.
|
|
58
|
+
The value can only be set when the entity is new. Any attempt to change it after
|
|
59
|
+
the first save throws an exception.
|
|
60
|
+
|
|
61
|
+
Use `writeOnceOn` for structural relationships that must never change, like
|
|
62
|
+
"which tenant owns this record":
|
|
63
|
+
|
|
64
|
+
```java
|
|
65
|
+
private final SQLEntityRef<SQLTenant> tenant =
|
|
66
|
+
SQLEntityRef.writeOnceOn(SQLTenant.class, BaseEntityRef.OnDelete.CASCADE);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Weak References
|
|
70
|
+
|
|
71
|
+
SQL refs can be made "weak" to skip foreign key constraint creation in the database.
|
|
72
|
+
This improves write performance but means the database will not enforce referential
|
|
73
|
+
integrity:
|
|
74
|
+
|
|
75
|
+
```java
|
|
76
|
+
private final SQLEntityRef<AuditLog> lastAudit =
|
|
77
|
+
SQLEntityRef.on(AuditLog.class, BaseEntityRef.OnDelete.IGNORE).weak();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## MongoRef<E>
|
|
81
|
+
|
|
82
|
+
For MongoDB entities, use `MongoRef<E>`. The ID type is `String`.
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
public class MongoOrderItem extends MongoTenantAware {
|
|
86
|
+
|
|
87
|
+
public static final Mapping ORDER = Mapping.named("order");
|
|
88
|
+
private final MongoRef<MongoOrder> order =
|
|
89
|
+
MongoRef.on(MongoOrder.class, BaseEntityRef.OnDelete.CASCADE);
|
|
90
|
+
|
|
91
|
+
public static final Mapping PRODUCT = Mapping.named("product");
|
|
92
|
+
private final MongoRef<MongoProduct> product =
|
|
93
|
+
MongoRef.on(MongoProduct.class, BaseEntityRef.OnDelete.REJECT);
|
|
94
|
+
|
|
95
|
+
// getters...
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`MongoRef` also supports `writeOnceOn()` with the same semantics as `SQLEntityRef`.
|
|
100
|
+
|
|
101
|
+
## Querying by Reference
|
|
102
|
+
|
|
103
|
+
To filter by a reference field, pass the referenced entity or its ID:
|
|
104
|
+
|
|
105
|
+
```java
|
|
106
|
+
// By entity
|
|
107
|
+
oma.select(OrderItem.class)
|
|
108
|
+
.eq(OrderItem.ORDER, order)
|
|
109
|
+
.queryList();
|
|
110
|
+
|
|
111
|
+
// By ID
|
|
112
|
+
oma.select(OrderItem.class)
|
|
113
|
+
.eq(OrderItem.ORDER, orderId)
|
|
114
|
+
.queryList();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Common Mistakes
|
|
118
|
+
|
|
119
|
+
1. **Using the wrong ref type** — `SQLEntityRef` is for `SQLEntity` subclasses;
|
|
120
|
+
`MongoRef` is for `MongoEntity` subclasses. Mixing them causes startup errors.
|
|
121
|
+
|
|
122
|
+
2. **Forgetting OnDelete** — Always choose an explicit `OnDelete` behavior.
|
|
123
|
+
`IGNORE` is rarely correct; it leaves dangling references. Prefer `CASCADE`
|
|
124
|
+
for owned children and `REJECT` for required dependencies.
|
|
125
|
+
|
|
126
|
+
3. **Mutating a writeOnce ref** — Attempting to change a `writeOnceOn` reference
|
|
127
|
+
after the entity has been persisted throws an exception. This is intentional;
|
|
128
|
+
restructure your logic if you need to change the relationship.
|
|
129
|
+
|
|
130
|
+
4. **Not declaring refs as final** — Reference fields should be `final`. The
|
|
131
|
+
framework mutates the ref's internal state (ID, cached value) but the ref
|
|
132
|
+
object itself must not be replaced.
|
|
133
|
+
|
|
134
|
+
5. **Calling getValue() in loops** — Each `getValue()` call may trigger a database
|
|
135
|
+
query. In loops, use batch loading or pre-fetch the related entities.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ResourceDefinition } from "./loader.js";
|
|
2
|
+
type LayerFilter = string;
|
|
3
|
+
/**
|
|
4
|
+
* Registry of all Sirius framework resource documents.
|
|
5
|
+
*
|
|
6
|
+
* Resources are markdown files organized by layer (kernel, web, db, biz).
|
|
7
|
+
* The registry loads all resources at construction time and provides
|
|
8
|
+
* methods for querying by layer or URI.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ResourceRegistry {
|
|
11
|
+
private resources;
|
|
12
|
+
private byUri;
|
|
13
|
+
constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Returns all resources whose layer is included in the given list.
|
|
16
|
+
*/
|
|
17
|
+
getResourcesForLayers(layers: LayerFilter[]): ResourceDefinition[];
|
|
18
|
+
/**
|
|
19
|
+
* Returns a single resource by its URI, or undefined if not found.
|
|
20
|
+
*/
|
|
21
|
+
getResource(uri: string): ResourceDefinition | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Returns all registered resources.
|
|
24
|
+
*/
|
|
25
|
+
getAllResources(): ResourceDefinition[];
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { loadResource } from "./loader.js";
|
|
2
|
+
/**
|
|
3
|
+
* Registry of all Sirius framework resource documents.
|
|
4
|
+
*
|
|
5
|
+
* Resources are markdown files organized by layer (kernel, web, db, biz).
|
|
6
|
+
* The registry loads all resources at construction time and provides
|
|
7
|
+
* methods for querying by layer or URI.
|
|
8
|
+
*/
|
|
9
|
+
export class ResourceRegistry {
|
|
10
|
+
resources;
|
|
11
|
+
byUri;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.resources = [
|
|
14
|
+
// Kernel (5)
|
|
15
|
+
loadResource("kernel", "di", "Dependency injection patterns: @Part, @Parts, @Register, @ConfigValue"),
|
|
16
|
+
loadResource("kernel", "lifecycle", "Lifecycle interfaces: Startable, Stoppable, Killable"),
|
|
17
|
+
loadResource("kernel", "async", "Async primitives: Tasks, CallContext, Promise, DelayLine"),
|
|
18
|
+
loadResource("kernel", "config", "Configuration system: HOCON, file precedence, framework flags"),
|
|
19
|
+
loadResource("kernel", "commons", "Common utilities: NLS, Strings, Value, Amount, Explain"),
|
|
20
|
+
// Web (3)
|
|
21
|
+
loadResource("web", "controllers", "Web controllers: routing, request handling, response rendering"),
|
|
22
|
+
loadResource("web", "services", "Web services: JSON APIs, ServiceCall, structured endpoints"),
|
|
23
|
+
loadResource("web", "templates", "Pasta/Tagliatelle template engine: syntax, tags, extensions"),
|
|
24
|
+
// DB (5)
|
|
25
|
+
loadResource("db", "entities", "Entity definitions: SQLEntity, MongoEntity, ElasticEntity"),
|
|
26
|
+
loadResource("db", "composites", "Composite pattern: reusable embedded data structures"),
|
|
27
|
+
loadResource("db", "refs", "References and foreign keys: EntityRef, BaseEntityRef"),
|
|
28
|
+
loadResource("db", "queries", "Query API: SmartQuery, filters, pagination, aggregation"),
|
|
29
|
+
loadResource("db", "mixing", "Mixing ORM: Mixins, annotations, schema management"),
|
|
30
|
+
// Biz (11)
|
|
31
|
+
loadResource("biz", "entity-triple", "Entity triple pattern: interface + SQL + Mongo implementations"),
|
|
32
|
+
loadResource("biz", "biz-controller", "BizController base class: tenant-aware controllers"),
|
|
33
|
+
loadResource("biz", "jobs", "Jobs framework: background job definitions, factories, parameters"),
|
|
34
|
+
loadResource("biz", "processes", "Process monitoring: logs, counters, state tracking"),
|
|
35
|
+
loadResource("biz", "importer", "Data import framework: ImportHandler, EntityImportHandler"),
|
|
36
|
+
loadResource("biz", "tenants", "Multi-tenant management: Tenant, UserAccount, permissions"),
|
|
37
|
+
loadResource("biz", "storage", "Object storage: Layer1 (physical), Layer2 (metadata), Layer3 (VFS)"),
|
|
38
|
+
loadResource("biz", "analytics", "Analytics: metrics, events, Clickhouse integration"),
|
|
39
|
+
loadResource("biz", "codelists", "Code lists: lookup tables, managed enumerations"),
|
|
40
|
+
loadResource("biz", "isenguard", "Isenguard: rate limiting, firewall, IP blocking"),
|
|
41
|
+
loadResource("biz", "testing", "Testing patterns: SiriusExtension, test entities, scenarios"),
|
|
42
|
+
];
|
|
43
|
+
this.byUri = new Map();
|
|
44
|
+
for (const resource of this.resources) {
|
|
45
|
+
this.byUri.set(resource.uri, resource);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns all resources whose layer is included in the given list.
|
|
50
|
+
*/
|
|
51
|
+
getResourcesForLayers(layers) {
|
|
52
|
+
const layerSet = new Set(layers.map((l) => l.toLowerCase()));
|
|
53
|
+
return this.resources.filter((r) => layerSet.has(r.layer));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Returns a single resource by its URI, or undefined if not found.
|
|
57
|
+
*/
|
|
58
|
+
getResource(uri) {
|
|
59
|
+
return this.byUri.get(uri);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns all registered resources.
|
|
63
|
+
*/
|
|
64
|
+
getAllResources() {
|
|
65
|
+
return [...this.resources];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAI/D;;;;;;GAMG;AACH,MAAM,OAAO,gBAAgB;IACnB,SAAS,CAAuB;IAChC,KAAK,CAAkC;IAE/C;QACE,IAAI,CAAC,SAAS,GAAG;YACf,aAAa;YACb,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,uEAAuE,CAAC;YACrG,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,sDAAsD,CAAC;YAC3F,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,0DAA0D,CAAC;YAC3F,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,+DAA+D,CAAC;YACjG,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,wDAAwD,CAAC;YAE3F,UAAU;YACV,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,gEAAgE,CAAC;YACpG,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,4DAA4D,CAAC;YAC7F,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,6DAA6D,CAAC;YAE/F,SAAS;YACT,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,2DAA2D,CAAC;YAC3F,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,sDAAsD,CAAC;YACxF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,uDAAuD,CAAC;YACnF,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,yDAAyD,CAAC;YACxF,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,oDAAoD,CAAC;YAElF,WAAW;YACX,YAAY,CAAC,KAAK,EAAE,eAAe,EAAE,gEAAgE,CAAC;YACtG,YAAY,CAAC,KAAK,EAAE,gBAAgB,EAAE,oDAAoD,CAAC;YAC3F,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,mEAAmE,CAAC;YAChG,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,oDAAoD,CAAC;YACtF,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,2DAA2D,CAAC;YAC5F,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,2DAA2D,CAAC;YAC3F,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,oEAAoE,CAAC;YACpG,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,oDAAoD,CAAC;YACtF,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,iDAAiD,CAAC;YACnF,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,iDAAiD,CAAC;YACnF,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,6DAA6D,CAAC;SAC9F,CAAC;QAEF,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,MAAqB;QACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,GAAW;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Async and Concurrency
|
|
2
|
+
|
|
3
|
+
Sirius-kernel provides its own concurrency primitives built around managed thread
|
|
4
|
+
pools, context propagation, and a promise/future model.
|
|
5
|
+
|
|
6
|
+
## Tasks Service
|
|
7
|
+
|
|
8
|
+
`Tasks` is the central service for running background work. It manages named
|
|
9
|
+
executor pools and ensures proper context propagation.
|
|
10
|
+
|
|
11
|
+
```java
|
|
12
|
+
@Part
|
|
13
|
+
private Tasks tasks;
|
|
14
|
+
|
|
15
|
+
// Run in the default executor
|
|
16
|
+
tasks.defaultExecutor().start(() -> {
|
|
17
|
+
// Background work — CallContext is automatically transferred
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Run in a named executor
|
|
21
|
+
tasks.executor("my-pool").start(() -> {
|
|
22
|
+
// Runs in a pool configured under "async.executor.my-pool"
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Executor Configuration
|
|
27
|
+
|
|
28
|
+
Executors are configured in HOCON:
|
|
29
|
+
|
|
30
|
+
```hocon
|
|
31
|
+
async.executor.my-pool {
|
|
32
|
+
poolSize = 4
|
|
33
|
+
maxSize = 16
|
|
34
|
+
queueLength = 100
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- `poolSize` — Core thread count.
|
|
39
|
+
- `maxSize` — Maximum thread count under load.
|
|
40
|
+
- `queueLength` — Work queue capacity. Tasks beyond this are rejected.
|
|
41
|
+
|
|
42
|
+
### Periodic Tasks
|
|
43
|
+
|
|
44
|
+
For recurring work, use `EveryMinute`, `EveryTenMinutes`, `EveryHour`, or
|
|
45
|
+
`EveryDay` by implementing the interface and registering:
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
@Register(classes = {CleanupTask.class, EveryHour.class})
|
|
49
|
+
public class CleanupTask implements EveryHour {
|
|
50
|
+
|
|
51
|
+
@Override
|
|
52
|
+
public void runTimer() throws Exception {
|
|
53
|
+
// Called every hour
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CallContext
|
|
59
|
+
|
|
60
|
+
`CallContext` is thread-local context that automatically transfers to child
|
|
61
|
+
threads managed by `Tasks`. It carries:
|
|
62
|
+
|
|
63
|
+
- The current **language** (for NLS translations).
|
|
64
|
+
- The current **user** and **tenant** (MDC-style context).
|
|
65
|
+
- A **TaskContext** for monitoring progress and cancellation.
|
|
66
|
+
- **Log output** and flow tracking.
|
|
67
|
+
|
|
68
|
+
```java
|
|
69
|
+
// Access current context
|
|
70
|
+
CallContext ctx = CallContext.getCurrent();
|
|
71
|
+
|
|
72
|
+
// Get a sub-context component
|
|
73
|
+
UserInfo currentUser = ctx.get(UserInfo.class);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
When `Tasks` dispatches work to a thread pool, it captures the caller's
|
|
77
|
+
`CallContext` and installs it in the worker thread. This means background
|
|
78
|
+
tasks automatically inherit the user, language, and logging context.
|
|
79
|
+
|
|
80
|
+
### TaskContext
|
|
81
|
+
|
|
82
|
+
Within a `CallContext`, the `TaskContext` tracks the current operation and
|
|
83
|
+
supports cooperative cancellation:
|
|
84
|
+
|
|
85
|
+
```java
|
|
86
|
+
TaskContext taskCtx = TaskContext.get();
|
|
87
|
+
|
|
88
|
+
// Check if the current task should stop
|
|
89
|
+
if (!taskCtx.isActive()) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Report progress
|
|
94
|
+
taskCtx.setState("Processing item %d of %d", current, total);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Long-running operations should periodically check `isActive()` to support
|
|
98
|
+
graceful cancellation.
|
|
99
|
+
|
|
100
|
+
## Promise and Future
|
|
101
|
+
|
|
102
|
+
Sirius provides its own `Promise<T>` as a lightweight alternative to
|
|
103
|
+
`CompletableFuture`:
|
|
104
|
+
|
|
105
|
+
```java
|
|
106
|
+
// Create a promise
|
|
107
|
+
Promise<String> promise = new Promise<>();
|
|
108
|
+
|
|
109
|
+
// Register handlers
|
|
110
|
+
promise.onSuccess(value -> {
|
|
111
|
+
// Called when the promise is fulfilled
|
|
112
|
+
});
|
|
113
|
+
promise.onFailure(error -> {
|
|
114
|
+
// Called when the promise fails
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Fulfill or fail
|
|
118
|
+
promise.success("result");
|
|
119
|
+
// or
|
|
120
|
+
promise.fail(new IOException("failed"));
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Blocking Wait
|
|
124
|
+
|
|
125
|
+
```java
|
|
126
|
+
String result = promise.await(Duration.ofSeconds(30));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`await()` blocks until the promise completes or the timeout expires.
|
|
130
|
+
|
|
131
|
+
### Chaining
|
|
132
|
+
|
|
133
|
+
```java
|
|
134
|
+
Promise<Integer> length = promise.map(String::length);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## DelayLine
|
|
138
|
+
|
|
139
|
+
`DelayLine` provides delayed execution — tasks are submitted and executed after
|
|
140
|
+
a configured delay:
|
|
141
|
+
|
|
142
|
+
```java
|
|
143
|
+
@Part
|
|
144
|
+
private DelayLine delayLine;
|
|
145
|
+
|
|
146
|
+
delayLine.callDelayed("my-token", Duration.ofMinutes(5), () -> {
|
|
147
|
+
// Executed 5 minutes from now
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The token parameter is used for deduplication — submitting a new task with the
|
|
152
|
+
same token cancels the previous one. This is useful for debouncing (e.g., only
|
|
153
|
+
send a notification if no further events arrive within 5 minutes).
|
|
154
|
+
|
|
155
|
+
## Orchestration
|
|
156
|
+
|
|
157
|
+
`Orchestration` coordinates distributed background work across cluster nodes.
|
|
158
|
+
It ensures that periodic tasks run on only one node in a cluster:
|
|
159
|
+
|
|
160
|
+
```java
|
|
161
|
+
@Register(classes = {MyDistributedTask.class, EveryHour.class})
|
|
162
|
+
public class MyDistributedTask implements EveryHour {
|
|
163
|
+
|
|
164
|
+
@Part
|
|
165
|
+
private Orchestration orchestration;
|
|
166
|
+
|
|
167
|
+
@Override
|
|
168
|
+
public void runTimer() throws Exception {
|
|
169
|
+
orchestration.runInCluster("my-task", () -> {
|
|
170
|
+
// Only executes on one node
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Best Practices
|
|
177
|
+
|
|
178
|
+
1. **Always use `Tasks`** for background work — never create raw threads.
|
|
179
|
+
`Tasks` ensures context propagation and proper executor management.
|
|
180
|
+
|
|
181
|
+
2. **Check `isActive()`** in long-running loops to support cancellation.
|
|
182
|
+
|
|
183
|
+
3. **Use DelayLine** for debounce patterns instead of `Thread.sleep`.
|
|
184
|
+
|
|
185
|
+
4. **Configure executor pools** explicitly. The default pool is shared and
|
|
186
|
+
should not be saturated with long-running tasks.
|
|
187
|
+
|
|
188
|
+
5. **Name your executors** descriptively. Executor names appear in thread dumps
|
|
189
|
+
and monitoring, making debugging much easier.
|