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,203 @@
|
|
|
1
|
+
# Commons Utilities
|
|
2
|
+
|
|
3
|
+
Sirius-kernel provides a set of utility classes in `sirius.kernel.commons` for
|
|
4
|
+
everyday programming tasks: internationalization, string handling, type-safe
|
|
5
|
+
value wrapping, and decimal arithmetic.
|
|
6
|
+
|
|
7
|
+
## NLS — Internationalization
|
|
8
|
+
|
|
9
|
+
`NLS` (Native Language Support) is the central class for i18n. Translations
|
|
10
|
+
are stored in `.properties` files and accessed via keys.
|
|
11
|
+
|
|
12
|
+
### Simple Lookup
|
|
13
|
+
|
|
14
|
+
```java
|
|
15
|
+
// Looks up key "MyEntity.name" in the current language
|
|
16
|
+
String label = NLS.get("MyEntity.name");
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Formatted Messages
|
|
20
|
+
|
|
21
|
+
`NLS.fmtr()` provides named-parameter formatting:
|
|
22
|
+
|
|
23
|
+
```java
|
|
24
|
+
// Given property: "welcome = Welcome, {{name}}! You have {{count}} items."
|
|
25
|
+
String msg = NLS.fmtr("welcome")
|
|
26
|
+
.set("name", userName)
|
|
27
|
+
.set("count", itemCount)
|
|
28
|
+
.format();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The `{{param}}` syntax is Sirius-specific — it uses named parameters rather
|
|
32
|
+
than positional ones, making translations much easier to maintain.
|
|
33
|
+
|
|
34
|
+
### Smart Formatting
|
|
35
|
+
|
|
36
|
+
`NLS.toUserString(Object)` converts any value to a locale-appropriate string:
|
|
37
|
+
- Numbers are formatted with locale-specific grouping and decimals.
|
|
38
|
+
- Dates/times use the locale's standard format.
|
|
39
|
+
- `Amount` values respect precision and locale.
|
|
40
|
+
|
|
41
|
+
### Property Files
|
|
42
|
+
|
|
43
|
+
Translation files follow the pattern `basename_lang.properties`:
|
|
44
|
+
- `biz_en.properties` — English
|
|
45
|
+
- `biz_de.properties` — German
|
|
46
|
+
|
|
47
|
+
They are loaded from the classpath under `resources/`.
|
|
48
|
+
|
|
49
|
+
## Strings
|
|
50
|
+
|
|
51
|
+
`Strings` provides null-safe string operations:
|
|
52
|
+
|
|
53
|
+
```java
|
|
54
|
+
// Null-safe emptiness checks
|
|
55
|
+
Strings.isEmpty(null); // true
|
|
56
|
+
Strings.isEmpty(""); // true
|
|
57
|
+
Strings.isEmpty(" "); // true (trims first)
|
|
58
|
+
Strings.isFilled(value); // opposite of isEmpty
|
|
59
|
+
|
|
60
|
+
// Null-safe equality
|
|
61
|
+
Strings.areEqual(a, b); // null == null is true
|
|
62
|
+
Strings.equalIgnoreCase(a, b);
|
|
63
|
+
|
|
64
|
+
// Join with separator, skipping empty values
|
|
65
|
+
Strings.join(", ", firstName, lastName); // Skips nulls/empty
|
|
66
|
+
|
|
67
|
+
// Apply a string operation only if filled
|
|
68
|
+
Strings.apply("%s (%s)", name, code); // Returns "" if name is empty
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Strings.join
|
|
72
|
+
|
|
73
|
+
A commonly used pattern for building display strings:
|
|
74
|
+
|
|
75
|
+
```java
|
|
76
|
+
// Produces "John, Doe" — skips empty parts
|
|
77
|
+
String fullName = Strings.join(" ", title, firstName, lastName);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Value
|
|
81
|
+
|
|
82
|
+
`Value` is a type-safe wrapper around an arbitrary object, providing fluent
|
|
83
|
+
conversion and null-safe access:
|
|
84
|
+
|
|
85
|
+
```java
|
|
86
|
+
Value val = Value.of(someObject);
|
|
87
|
+
|
|
88
|
+
// Type conversions with defaults
|
|
89
|
+
String s = val.asString(); // "" if null
|
|
90
|
+
int n = val.asInt(0); // 0 if not convertible
|
|
91
|
+
boolean b = val.asBoolean(); // false if null
|
|
92
|
+
LocalDate d = val.asLocalDate(fallback);
|
|
93
|
+
|
|
94
|
+
// Check state
|
|
95
|
+
val.isNull();
|
|
96
|
+
val.isFilled();
|
|
97
|
+
val.isEmpty();
|
|
98
|
+
|
|
99
|
+
// Conditional execution
|
|
100
|
+
val.ifFilled(v -> process(v));
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`Value` is used extensively in the web layer for request parameters and in the
|
|
104
|
+
import framework for cell values.
|
|
105
|
+
|
|
106
|
+
## Amount
|
|
107
|
+
|
|
108
|
+
`Amount` provides precise decimal arithmetic, replacing `BigDecimal` with a
|
|
109
|
+
more ergonomic API. It is the standard type for monetary values and quantities.
|
|
110
|
+
|
|
111
|
+
```java
|
|
112
|
+
Amount price = Amount.of(19.99);
|
|
113
|
+
Amount quantity = Amount.of(3);
|
|
114
|
+
|
|
115
|
+
// Arithmetic
|
|
116
|
+
Amount total = price.times(quantity); // 59.97
|
|
117
|
+
Amount discounted = total.subtract(Amount.of(5));
|
|
118
|
+
|
|
119
|
+
// Rounding
|
|
120
|
+
Amount rounded = total.round(RoundingMode.HALF_UP, 2);
|
|
121
|
+
|
|
122
|
+
// Comparison
|
|
123
|
+
total.isGreaterThan(Amount.of(50)); // true
|
|
124
|
+
total.isZeroOrNull(); // false
|
|
125
|
+
|
|
126
|
+
// Safe null handling — Amount.NOTHING represents "no value"
|
|
127
|
+
Amount.NOTHING.add(Amount.of(10)); // Amount.of(10)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Amount vs BigDecimal
|
|
131
|
+
|
|
132
|
+
`Amount` wraps `BigDecimal` but adds:
|
|
133
|
+
- Null safety via `Amount.NOTHING` (acts as identity in arithmetic).
|
|
134
|
+
- Fluent API: `a.add(b).times(c)` reads naturally.
|
|
135
|
+
- Built-in formatting: `amount.toSmartRoundedString()`.
|
|
136
|
+
|
|
137
|
+
## Formatter (Strings.apply)
|
|
138
|
+
|
|
139
|
+
`Strings.apply()` provides printf-like formatting but with Sirius conventions:
|
|
140
|
+
|
|
141
|
+
```java
|
|
142
|
+
Strings.apply("User %s logged in from %s", username, ip);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
For more complex cases, use `NLS.fmtr()` with named parameters.
|
|
146
|
+
|
|
147
|
+
## @Explain Annotation
|
|
148
|
+
|
|
149
|
+
`@Explain` is a documentation annotation used to justify code decisions that
|
|
150
|
+
might otherwise trigger review comments or static analysis warnings:
|
|
151
|
+
|
|
152
|
+
```java
|
|
153
|
+
@Explain("We use a constant here because the interface pattern is intentional")
|
|
154
|
+
public interface Permissions {
|
|
155
|
+
String PERMISSION_MANAGE_USERS = "permission-manage-users";
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
It has no runtime effect — it serves purely as inline documentation for
|
|
160
|
+
reviewers and maintainers. Common uses:
|
|
161
|
+
- Constants in interfaces (a Sirius convention).
|
|
162
|
+
- Intentional fallthrough in switch statements.
|
|
163
|
+
- Suppressed warnings that need justification.
|
|
164
|
+
|
|
165
|
+
## Tuple and MultiLanguageString
|
|
166
|
+
|
|
167
|
+
### Tuple
|
|
168
|
+
|
|
169
|
+
A simple pair container:
|
|
170
|
+
|
|
171
|
+
```java
|
|
172
|
+
Tuple<String, Integer> pair = Tuple.create("key", 42);
|
|
173
|
+
pair.getFirst(); // "key"
|
|
174
|
+
pair.getSecond(); // 42
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### MultiLanguageString
|
|
178
|
+
|
|
179
|
+
Stores text in multiple languages:
|
|
180
|
+
|
|
181
|
+
```java
|
|
182
|
+
MultiLanguageString mls = new MultiLanguageString();
|
|
183
|
+
mls.addText("en", "Hello");
|
|
184
|
+
mls.addText("de", "Hallo");
|
|
185
|
+
|
|
186
|
+
// Gets text for current language, falls back to default
|
|
187
|
+
String text = mls.getText();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Used in entities that need multi-language content (product names, descriptions).
|
|
191
|
+
|
|
192
|
+
## Best Practices
|
|
193
|
+
|
|
194
|
+
1. **Use `Strings.isFilled()`** instead of `str != null && !str.isEmpty()`.
|
|
195
|
+
|
|
196
|
+
2. **Use `Amount`** for all monetary and quantity values — never `double`.
|
|
197
|
+
|
|
198
|
+
3. **Use `NLS.fmtr()`** with named parameters for user-facing messages. Positional
|
|
199
|
+
parameters break when translations reorder words.
|
|
200
|
+
|
|
201
|
+
4. **Use `Value`** when dealing with loosely typed data (web params, imports).
|
|
202
|
+
|
|
203
|
+
5. **Use `@Explain`** to document non-obvious code choices proactively.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Configuration System
|
|
2
|
+
|
|
3
|
+
Sirius uses Typesafe Config (HOCON format) for all configuration. The system
|
|
4
|
+
supports layered config files with well-defined precedence and a framework
|
|
5
|
+
flag mechanism for enabling/disabling modules.
|
|
6
|
+
|
|
7
|
+
## HOCON Format
|
|
8
|
+
|
|
9
|
+
HOCON (Human-Optimized Config Object Notation) is a superset of JSON:
|
|
10
|
+
|
|
11
|
+
```hocon
|
|
12
|
+
http {
|
|
13
|
+
port = 9000
|
|
14
|
+
sessionTimeout = 30 minutes
|
|
15
|
+
ssl.enabled = false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Lists
|
|
19
|
+
allowed.hosts = ["localhost", "example.com"]
|
|
20
|
+
|
|
21
|
+
# Substitutions
|
|
22
|
+
app.url = "http://"${http.host}":"${http.port}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Key features: comments with `#`, duration/size literals (`30 minutes`, `512MB`),
|
|
26
|
+
path expressions with dots, and variable substitution.
|
|
27
|
+
|
|
28
|
+
## File Precedence
|
|
29
|
+
|
|
30
|
+
Config files are loaded in this order, with later files overriding earlier ones:
|
|
31
|
+
|
|
32
|
+
1. **`component-NNN-name.conf`** — Framework defaults, loaded by classpath scan.
|
|
33
|
+
The numeric prefix controls load order (e.g., `component-050-kernel.conf`
|
|
34
|
+
loads before `component-070-biz.conf`).
|
|
35
|
+
|
|
36
|
+
2. **`application.conf`** — Application-level defaults. This is where a Sirius
|
|
37
|
+
application defines its own configuration.
|
|
38
|
+
|
|
39
|
+
3. **`develop.conf`** — Development overrides. Loaded only when the system
|
|
40
|
+
property `sirius.debug` is set (automatically set in dev mode). Use this
|
|
41
|
+
for local database URLs, debug flags, etc.
|
|
42
|
+
|
|
43
|
+
4. **`instance.conf`** — Instance-specific configuration. Loaded last, used
|
|
44
|
+
for production secrets, deployment-specific URLs, and other environment
|
|
45
|
+
settings. Typically not checked into version control.
|
|
46
|
+
|
|
47
|
+
### Merge Behavior
|
|
48
|
+
|
|
49
|
+
Later files **merge** with earlier ones — they do not replace the entire tree.
|
|
50
|
+
A key in `instance.conf` overrides only that specific key; everything else
|
|
51
|
+
from earlier files remains.
|
|
52
|
+
|
|
53
|
+
## Framework Flags
|
|
54
|
+
|
|
55
|
+
The `sirius.frameworks` config section controls which modules are active:
|
|
56
|
+
|
|
57
|
+
```hocon
|
|
58
|
+
sirius.frameworks {
|
|
59
|
+
biz.tenants = true
|
|
60
|
+
biz.tenants-jdbc = true
|
|
61
|
+
biz.tenants-mongo = false
|
|
62
|
+
biz.storage = true
|
|
63
|
+
biz.jobs = true
|
|
64
|
+
biz.processes = true
|
|
65
|
+
biz.analytics = false
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**All flags default to `false`.** A module must be explicitly enabled by the
|
|
70
|
+
application or it will not be loaded.
|
|
71
|
+
|
|
72
|
+
Framework flags affect two things:
|
|
73
|
+
|
|
74
|
+
1. **Entity loading** — Entities annotated with `@Framework("flag.name")` are
|
|
75
|
+
only registered in the ORM when the flag is `true`.
|
|
76
|
+
|
|
77
|
+
2. **Service loading** — Services annotated with `@Register(framework = "flag.name")`
|
|
78
|
+
are only instantiated and registered when the flag is `true`.
|
|
79
|
+
|
|
80
|
+
### Naming Convention
|
|
81
|
+
|
|
82
|
+
Framework flags follow the pattern `module.feature` or `module.feature-variant`:
|
|
83
|
+
- `biz.tenants` — The tenants module
|
|
84
|
+
- `biz.tenants-jdbc` — JDBC variant of tenants
|
|
85
|
+
- `biz.tenants-mongo` — MongoDB variant of tenants
|
|
86
|
+
|
|
87
|
+
## @ConfigValue Injection
|
|
88
|
+
|
|
89
|
+
Configuration values can be injected directly into fields:
|
|
90
|
+
|
|
91
|
+
```java
|
|
92
|
+
@ConfigValue("http.port")
|
|
93
|
+
private int port;
|
|
94
|
+
|
|
95
|
+
@ConfigValue("product.name")
|
|
96
|
+
private String productName;
|
|
97
|
+
|
|
98
|
+
@ConfigValue("feature.enabled")
|
|
99
|
+
private boolean enabled;
|
|
100
|
+
|
|
101
|
+
@ConfigValue("http.sessionTimeout")
|
|
102
|
+
private Duration sessionTimeout;
|
|
103
|
+
|
|
104
|
+
@ConfigValue("allowed.hosts")
|
|
105
|
+
private List<String> hosts;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The injection happens at startup time and values are immutable after that.
|
|
109
|
+
|
|
110
|
+
## Accessing Config Programmatically
|
|
111
|
+
|
|
112
|
+
For dynamic access, use the `Sirius` class:
|
|
113
|
+
|
|
114
|
+
```java
|
|
115
|
+
// Get a config value
|
|
116
|
+
String value = Sirius.getSettings().getString("my.config.key");
|
|
117
|
+
|
|
118
|
+
// Check if a framework is enabled
|
|
119
|
+
boolean enabled = Sirius.isFrameworkEnabled("biz.tenants");
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Extension Configs
|
|
123
|
+
|
|
124
|
+
Libraries can provide default configuration by including a
|
|
125
|
+
`component-NNN-name.conf` file in their JAR's classpath root. The naming
|
|
126
|
+
convention:
|
|
127
|
+
|
|
128
|
+
- `component-050-kernel.conf` — sirius-kernel defaults
|
|
129
|
+
- `component-060-web.conf` — sirius-web defaults
|
|
130
|
+
- `component-060-db.conf` — sirius-db defaults
|
|
131
|
+
- `component-070-biz.conf` — sirius-biz defaults
|
|
132
|
+
|
|
133
|
+
The numbering ensures correct load order: kernel before web, web before biz.
|
|
134
|
+
|
|
135
|
+
## System Properties
|
|
136
|
+
|
|
137
|
+
Some settings can be overridden via JVM system properties:
|
|
138
|
+
|
|
139
|
+
- `-Dsirius.debug=true` — Enables development mode (loads `develop.conf`)
|
|
140
|
+
- `-Dport=9000` — Override HTTP port
|
|
141
|
+
- `-Dsirius.nodeName=node1` — Set the cluster node name
|
|
142
|
+
|
|
143
|
+
## Best Practices
|
|
144
|
+
|
|
145
|
+
1. **Never hardcode values** that might vary between environments. Use config.
|
|
146
|
+
|
|
147
|
+
2. **Put secrets in `instance.conf`** and exclude it from version control.
|
|
148
|
+
|
|
149
|
+
3. **Use `develop.conf`** for local database URLs and debug settings.
|
|
150
|
+
|
|
151
|
+
4. **Document config keys** in your `component-*.conf` with comments — these
|
|
152
|
+
serve as the default values and documentation for downstream users.
|
|
153
|
+
|
|
154
|
+
5. **Check framework flags** before depending on a module's services. If your
|
|
155
|
+
code is optional, guard it with a framework flag on your own `@Register`.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Dependency Injection
|
|
2
|
+
|
|
3
|
+
Sirius uses its own lightweight DI system built into sirius-kernel. All injection
|
|
4
|
+
happens at startup time — there is no runtime resolution or request-scoped injection.
|
|
5
|
+
|
|
6
|
+
## @Register — Publishing a Service
|
|
7
|
+
|
|
8
|
+
Every class that should participate in DI must be annotated with `@Register`:
|
|
9
|
+
|
|
10
|
+
```java
|
|
11
|
+
@Register(classes = {MyService.class, Startable.class})
|
|
12
|
+
public class MyServiceImpl implements MyService, Startable {
|
|
13
|
+
// ...
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Critical rule:** The `classes` parameter must list **every interface and superclass**
|
|
18
|
+
the component should be discoverable as. If you implement `Startable` but omit it
|
|
19
|
+
from `classes`, the framework will never call `started()`.
|
|
20
|
+
|
|
21
|
+
Parameters:
|
|
22
|
+
- `classes` — Array of types this component registers as (required when implementing interfaces).
|
|
23
|
+
- `framework` — The framework flag that must be enabled for this component to load
|
|
24
|
+
(e.g., `@Register(framework = "biz.tenants")`).
|
|
25
|
+
- `name` — Optional name for named lookups.
|
|
26
|
+
|
|
27
|
+
If a class has no interfaces and no special lifecycle, a bare `@Register` suffices.
|
|
28
|
+
|
|
29
|
+
## @Framework — For Entities
|
|
30
|
+
|
|
31
|
+
Entities (database-mapped classes) use `@Framework` instead of `@Register(framework = ...)`:
|
|
32
|
+
|
|
33
|
+
```java
|
|
34
|
+
@Framework("biz.tenants-jdbc")
|
|
35
|
+
public class SQLTenant extends SQLTenantAware<SQLTenant, SQLUserAccount>
|
|
36
|
+
implements Tenant<Long> {
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The distinction: `@Register(framework = ...)` controls service loading;
|
|
41
|
+
`@Framework` controls entity registration in the schema and ORM layer.
|
|
42
|
+
|
|
43
|
+
## @Part — Injecting a Singleton
|
|
44
|
+
|
|
45
|
+
`@Part` injects a single implementation of a type:
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
@Part
|
|
49
|
+
private OMA oma;
|
|
50
|
+
|
|
51
|
+
@Part
|
|
52
|
+
private Tasks tasks;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The field is populated after the object is constructed, before `started()` is called.
|
|
56
|
+
|
|
57
|
+
If no implementation is registered, the field remains `null` — no error is thrown.
|
|
58
|
+
Guard against this in optional dependencies.
|
|
59
|
+
|
|
60
|
+
### configPath
|
|
61
|
+
|
|
62
|
+
For interfaces with multiple named implementations, use `configPath`:
|
|
63
|
+
|
|
64
|
+
```java
|
|
65
|
+
@Part(configPath = "storage.layer1.engine")
|
|
66
|
+
private ObjectStorageEngine engine;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This reads the implementation class name from the config file, allowing runtime
|
|
70
|
+
selection of the concrete implementation.
|
|
71
|
+
|
|
72
|
+
## @Parts — Injecting All Implementations
|
|
73
|
+
|
|
74
|
+
`@Parts` injects every registered implementation of a given interface as a
|
|
75
|
+
`PartCollection`:
|
|
76
|
+
|
|
77
|
+
```java
|
|
78
|
+
@Parts(SidebarProvider.class)
|
|
79
|
+
private PartCollection<SidebarProvider> providers;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`PartCollection` is iterable and provides `getAll()` returning a list. The
|
|
83
|
+
implementations are returned in no guaranteed order.
|
|
84
|
+
|
|
85
|
+
## @PriorityParts — Ordered Injection
|
|
86
|
+
|
|
87
|
+
Like `@Parts`, but returns implementations sorted by their `Priorized.getPriority()`
|
|
88
|
+
value (lower values first):
|
|
89
|
+
|
|
90
|
+
```java
|
|
91
|
+
@PriorityParts(LinkTarget.class)
|
|
92
|
+
private List<LinkTarget> targets;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use this when execution order matters — e.g., filter chains, interceptors, or
|
|
96
|
+
fallback strategies.
|
|
97
|
+
|
|
98
|
+
## @ConfigValue — Injecting Configuration
|
|
99
|
+
|
|
100
|
+
`@ConfigValue` injects values from the Typesafe Config system:
|
|
101
|
+
|
|
102
|
+
```java
|
|
103
|
+
@ConfigValue("product.baseUrl")
|
|
104
|
+
private String baseUrl;
|
|
105
|
+
|
|
106
|
+
@ConfigValue("http.sessionTimeout")
|
|
107
|
+
private Duration sessionTimeout;
|
|
108
|
+
|
|
109
|
+
@ConfigValue("security.enabled")
|
|
110
|
+
private boolean securityEnabled;
|
|
111
|
+
|
|
112
|
+
@ConfigValue("cache.maxSize")
|
|
113
|
+
private int maxSize;
|
|
114
|
+
|
|
115
|
+
@ConfigValue("allowed.hosts")
|
|
116
|
+
private List<String> allowedHosts;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Supported types: `String`, `int`, `long`, `boolean`, `Duration`, `List<String>`.
|
|
120
|
+
The config path refers to a key in the HOCON configuration (see config resource).
|
|
121
|
+
|
|
122
|
+
## Common Mistakes
|
|
123
|
+
|
|
124
|
+
1. **Missing `classes` in @Register** — The most frequent DI bug. If your class
|
|
125
|
+
implements `Startable` or `Initializable` and you write `@Register` without
|
|
126
|
+
listing those interfaces in `classes`, the lifecycle methods will never be called.
|
|
127
|
+
|
|
128
|
+
2. **Circular dependencies** — Sirius does not support circular `@Part` injection.
|
|
129
|
+
Use `Injector.context().findPart(...)` for lazy resolution when needed.
|
|
130
|
+
|
|
131
|
+
3. **Using DI in constructors** — `@Part` fields are not yet populated during
|
|
132
|
+
construction. Use `Startable.started()` or `Initializable.initialize()` for
|
|
133
|
+
init logic that depends on injected parts.
|
|
134
|
+
|
|
135
|
+
4. **Forgetting the framework flag** — If your service only makes sense when a
|
|
136
|
+
specific framework is active, always set `@Register(framework = "...")`.
|
|
137
|
+
Otherwise the class loads unconditionally and may fail if its dependencies
|
|
138
|
+
are not present.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Lifecycle Interfaces
|
|
2
|
+
|
|
3
|
+
Sirius-kernel defines three lifecycle interfaces that components can implement to
|
|
4
|
+
hook into the application startup and shutdown process. All three extend `Priorized`,
|
|
5
|
+
which controls execution order.
|
|
6
|
+
|
|
7
|
+
## Startable
|
|
8
|
+
|
|
9
|
+
`Startable` is called once after dependency injection is complete. This is the
|
|
10
|
+
primary hook for initialization logic that depends on injected `@Part` fields.
|
|
11
|
+
|
|
12
|
+
```java
|
|
13
|
+
@Register(classes = {MyService.class, Startable.class})
|
|
14
|
+
public class MyServiceImpl implements MyService, Startable {
|
|
15
|
+
|
|
16
|
+
@Part
|
|
17
|
+
private OMA oma;
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public int getPriority() {
|
|
21
|
+
return PriorityCollector.DEFAULT_PRIORITY;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public void started() {
|
|
26
|
+
// All @Part fields are populated at this point.
|
|
27
|
+
// Initialize caches, start background work, etc.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Key points:**
|
|
33
|
+
- `started()` is called exactly once, during framework boot.
|
|
34
|
+
- All `@Part` fields are guaranteed to be injected before `started()` runs.
|
|
35
|
+
- Components are started in priority order (lower `getPriority()` values first).
|
|
36
|
+
- The `started()` method should not block indefinitely — long-running work should
|
|
37
|
+
be dispatched to an executor via the `Tasks` service.
|
|
38
|
+
|
|
39
|
+
## Stoppable
|
|
40
|
+
|
|
41
|
+
`Stoppable` is called during graceful shutdown. It **must not block** for extended
|
|
42
|
+
periods — it should signal threads to stop, release non-critical resources, and
|
|
43
|
+
return quickly.
|
|
44
|
+
|
|
45
|
+
```java
|
|
46
|
+
@Register(classes = {CacheManager.class, Startable.class, Stoppable.class})
|
|
47
|
+
public class CacheManager implements Startable, Stoppable {
|
|
48
|
+
|
|
49
|
+
@Override
|
|
50
|
+
public void started() {
|
|
51
|
+
// Initialize caches
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Override
|
|
55
|
+
public void stopped() {
|
|
56
|
+
// Signal cache eviction threads to stop.
|
|
57
|
+
// Must return quickly — do NOT block here.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Key points:**
|
|
63
|
+
- `stopped()` is called in reverse priority order (highest priority stopped first).
|
|
64
|
+
- Must not block. If you need to wait for cleanup, implement `Killable` instead.
|
|
65
|
+
- Called before `Killable.killed()`, so resources may still be partially available.
|
|
66
|
+
|
|
67
|
+
## Killable
|
|
68
|
+
|
|
69
|
+
`Killable` is the final shutdown hook. Unlike `Stoppable`, it **may block** to
|
|
70
|
+
perform thorough cleanup — closing database connections, flushing buffers,
|
|
71
|
+
writing final state.
|
|
72
|
+
|
|
73
|
+
```java
|
|
74
|
+
@Register(classes = {ConnectionPool.class, Startable.class, Killable.class})
|
|
75
|
+
public class ConnectionPool implements Startable, Killable {
|
|
76
|
+
|
|
77
|
+
@Override
|
|
78
|
+
public void started() {
|
|
79
|
+
// Open connection pool
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@Override
|
|
83
|
+
public void killed() {
|
|
84
|
+
// Close all connections, wait for pending queries.
|
|
85
|
+
// Blocking is acceptable here.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Key points:**
|
|
91
|
+
- `killed()` is called after all `Stoppable` instances have been stopped.
|
|
92
|
+
- May block for cleanup — this is the last chance before the JVM exits.
|
|
93
|
+
- Called in reverse priority order.
|
|
94
|
+
|
|
95
|
+
## Priorized
|
|
96
|
+
|
|
97
|
+
All lifecycle interfaces extend `Priorized`:
|
|
98
|
+
|
|
99
|
+
```java
|
|
100
|
+
public interface Priorized {
|
|
101
|
+
int getPriority();
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Priority values control execution order:
|
|
106
|
+
- **Startup**: lower values start first (priority 10 starts before priority 100).
|
|
107
|
+
- **Shutdown**: higher values stop/kill first (reverse order).
|
|
108
|
+
|
|
109
|
+
Common priority constants from `PriorityCollector`:
|
|
110
|
+
- `DEFAULT_PRIORITY` (100) — Use unless you have a specific ordering need.
|
|
111
|
+
|
|
112
|
+
## Registration Pattern
|
|
113
|
+
|
|
114
|
+
The most important pattern: always list lifecycle interfaces in `@Register(classes = ...)`:
|
|
115
|
+
|
|
116
|
+
```java
|
|
117
|
+
// CORRECT
|
|
118
|
+
@Register(classes = {MyService.class, Startable.class, Stoppable.class})
|
|
119
|
+
public class MyServiceImpl implements MyService, Startable, Stoppable { ... }
|
|
120
|
+
|
|
121
|
+
// WRONG — lifecycle methods will never be called!
|
|
122
|
+
@Register
|
|
123
|
+
public class MyServiceImpl implements MyService, Startable, Stoppable { ... }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This is a common mistake. The framework discovers lifecycle participants by
|
|
127
|
+
looking up registered classes — if `Startable.class` is not in the `classes`
|
|
128
|
+
list, the framework does not know the component is startable.
|
|
129
|
+
|
|
130
|
+
## Initialization Order Summary
|
|
131
|
+
|
|
132
|
+
1. All `@Register` components are instantiated.
|
|
133
|
+
2. All `@Part` fields are injected.
|
|
134
|
+
3. `Startable.started()` is called in priority order (ascending).
|
|
135
|
+
4. Application runs.
|
|
136
|
+
5. Shutdown signal received.
|
|
137
|
+
6. `Stoppable.stopped()` is called in reverse priority order (non-blocking).
|
|
138
|
+
7. `Killable.killed()` is called in reverse priority order (may block).
|
|
139
|
+
8. JVM exits.
|
|
140
|
+
|
|
141
|
+
## Initializable
|
|
142
|
+
|
|
143
|
+
A less common alternative: `Initializable` provides an `initialize()` method
|
|
144
|
+
that is called even earlier than `started()`, during the injection phase itself.
|
|
145
|
+
Use this only when your component must be ready before other `@Part` injections
|
|
146
|
+
complete. In most cases, prefer `Startable`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
export function loadResource(layer, name, description) {
|
|
6
|
+
const filePath = join(__dirname, layer, `${name}.md`);
|
|
7
|
+
const content = readFileSync(filePath, "utf-8");
|
|
8
|
+
return {
|
|
9
|
+
uri: `sirius://${layer}/${name}`,
|
|
10
|
+
name: `${layer}/${name}`,
|
|
11
|
+
description,
|
|
12
|
+
layer,
|
|
13
|
+
mimeType: "text/markdown",
|
|
14
|
+
content,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/resources/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAW1D,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,IAAY,EACZ,WAAmB;IAEnB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,OAAO;QACL,GAAG,EAAE,YAAY,KAAK,IAAI,IAAI,EAAE;QAChC,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE;QACxB,WAAW;QACX,KAAK;QACL,QAAQ,EAAE,eAAe;QACzB,OAAO;KACR,CAAC;AACJ,CAAC"}
|