@twin.org/engine-core 0.0.2-next.9 → 0.0.3-next.2
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/dist/{esm/index.mjs → es/engineCore.js} +205 -266
- package/dist/es/engineCore.js.map +1 -0
- package/dist/es/index.js +8 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/IEngineCoreOptions.js +2 -0
- package/dist/es/models/IEngineCoreOptions.js.map +1 -0
- package/dist/es/storage/fileStateStorage.js +92 -0
- package/dist/es/storage/fileStateStorage.js.map +1 -0
- package/dist/es/storage/memoryStateStorage.js +55 -0
- package/dist/es/storage/memoryStateStorage.js.map +1 -0
- package/dist/es/utils/engineModuleHelper.js +46 -0
- package/dist/es/utils/engineModuleHelper.js.map +1 -0
- package/dist/types/engineCore.d.ts +51 -7
- package/dist/types/index.d.ts +5 -4
- package/dist/types/models/IEngineCoreOptions.d.ts +0 -5
- package/dist/types/storage/fileStateStorage.d.ts +1 -1
- package/dist/types/storage/memoryStateStorage.d.ts +1 -1
- package/dist/types/utils/engineModuleHelper.d.ts +17 -0
- package/docs/changelog.md +290 -0
- package/docs/reference/classes/EngineCore.md +156 -14
- package/docs/reference/classes/EngineModuleHelper.md +55 -0
- package/docs/reference/classes/FileStateStorage.md +1 -1
- package/docs/reference/classes/MemoryStateStorage.md +1 -1
- package/docs/reference/index.md +1 -0
- package/docs/reference/interfaces/IEngineCoreOptions.md +0 -14
- package/locales/en.json +10 -10
- package/package.json +23 -9
- package/dist/cjs/index.cjs +0 -649
|
@@ -1,91 +1,42 @@
|
|
|
1
|
-
import { isMainThread } from 'node:worker_threads';
|
|
2
|
-
import { I18n, StringHelper, Is, BaseError, GeneralError, ErrorHelper, Guards, ComponentFactory } from '@twin.org/core';
|
|
3
|
-
import { EntitySchemaFactory } from '@twin.org/entity';
|
|
4
|
-
import { ConsoleLoggingConnector } from '@twin.org/logging-connector-console';
|
|
5
|
-
import { SilentLoggingConnector, LoggingConnectorFactory } from '@twin.org/logging-models';
|
|
6
|
-
import { LoggingService } from '@twin.org/logging-service';
|
|
7
|
-
import { ModuleHelper } from '@twin.org/modules';
|
|
8
|
-
import { readFile, mkdir, writeFile, stat } from 'node:fs/promises';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
|
|
11
|
-
// Copyright 2024 IOTA Stiftung.
|
|
12
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
13
|
-
/**
|
|
14
|
-
* Store state in memory.
|
|
15
|
-
*/
|
|
16
|
-
class MemoryStateStorage {
|
|
17
|
-
/**
|
|
18
|
-
* Runtime name for the class.
|
|
19
|
-
*/
|
|
20
|
-
CLASS_NAME = "MemoryStateStorage";
|
|
21
|
-
/**
|
|
22
|
-
* Readonly mode state file is not updated.
|
|
23
|
-
* @internal
|
|
24
|
-
*/
|
|
25
|
-
_readonlyMode;
|
|
26
|
-
/**
|
|
27
|
-
* The state object.
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
_engineState;
|
|
31
|
-
/**
|
|
32
|
-
* Create a new instance of MemoryStateStorage.
|
|
33
|
-
* @param readonlyMode Whether the file is in read-only mode.
|
|
34
|
-
* @param state The initial state.
|
|
35
|
-
*/
|
|
36
|
-
constructor(readonlyMode = false, state) {
|
|
37
|
-
this._readonlyMode = readonlyMode;
|
|
38
|
-
this._engineState = state;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Method for loading the state.
|
|
42
|
-
* @param engineCore The engine core to load the state for.
|
|
43
|
-
* @returns The state of the engine or undefined if it doesn't exist.
|
|
44
|
-
*/
|
|
45
|
-
async load(engineCore) {
|
|
46
|
-
engineCore.logInfo(I18n.formatMessage(`${StringHelper.camelCase(this.CLASS_NAME)}.loading`, {
|
|
47
|
-
filename: this._engineState
|
|
48
|
-
}));
|
|
49
|
-
return this._engineState;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Method for saving the state.
|
|
53
|
-
* @param engineCore The engine core to save the state for.
|
|
54
|
-
* @param state The state of the engine to save.
|
|
55
|
-
* @returns Nothing.
|
|
56
|
-
*/
|
|
57
|
-
async save(engineCore, state) {
|
|
58
|
-
if (!this._readonlyMode) {
|
|
59
|
-
engineCore.logInfo(I18n.formatMessage(`${StringHelper.camelCase(this.CLASS_NAME)}.saving`));
|
|
60
|
-
this._engineState = state;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
1
|
// Copyright 2024 IOTA Stiftung.
|
|
66
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { isMainThread } from "node:worker_threads";
|
|
4
|
+
import { ContextIdStore } from "@twin.org/context";
|
|
5
|
+
import { BaseError, ComponentFactory, ErrorHelper, GeneralError, Guards, I18n, Is } from "@twin.org/core";
|
|
6
|
+
import { EntitySchemaFactory } from "@twin.org/entity";
|
|
7
|
+
import { ConsoleLoggingConnector } from "@twin.org/logging-connector-console";
|
|
8
|
+
import { LoggingConnectorFactory, SilentLoggingConnector } from "@twin.org/logging-models";
|
|
9
|
+
import { LoggingService } from "@twin.org/logging-service";
|
|
10
|
+
import { ModuleHelper } from "@twin.org/modules";
|
|
11
|
+
import { MemoryStateStorage } from "./storage/memoryStateStorage.js";
|
|
67
12
|
/**
|
|
68
13
|
* Core for the engine.
|
|
69
14
|
*/
|
|
70
|
-
class EngineCore {
|
|
15
|
+
export class EngineCore {
|
|
71
16
|
/**
|
|
72
|
-
* Name for the engine logger.
|
|
17
|
+
* Name for the engine logger component, used for direct console logging.
|
|
73
18
|
*/
|
|
74
|
-
static
|
|
19
|
+
static LOGGING_COMPONENT_TYPE_NAME = "engine-logging-service";
|
|
75
20
|
/**
|
|
76
|
-
*
|
|
77
|
-
* @internal
|
|
21
|
+
* Name for the engine logger connector, used for direct console logging.
|
|
78
22
|
*/
|
|
79
|
-
static
|
|
23
|
+
static LOGGING_CONNECTOR_TYPE_NAME = "engine-logging-connector";
|
|
80
24
|
/**
|
|
81
25
|
* Runtime name for the class.
|
|
82
|
-
* @internal
|
|
83
26
|
*/
|
|
84
|
-
static
|
|
27
|
+
static CLASS_NAME = "EngineCore";
|
|
85
28
|
/**
|
|
86
29
|
* The core context.
|
|
87
30
|
*/
|
|
88
31
|
_context;
|
|
32
|
+
/**
|
|
33
|
+
* The context ID keys.
|
|
34
|
+
*/
|
|
35
|
+
_contextIdKeys;
|
|
36
|
+
/**
|
|
37
|
+
* The context IDs.
|
|
38
|
+
*/
|
|
39
|
+
_contextIds;
|
|
89
40
|
/**
|
|
90
41
|
* The state storage interface.
|
|
91
42
|
* @internal
|
|
@@ -101,11 +52,6 @@ class EngineCore {
|
|
|
101
52
|
* @internal
|
|
102
53
|
*/
|
|
103
54
|
_skipBootstrap;
|
|
104
|
-
/**
|
|
105
|
-
* The logger type name to use.
|
|
106
|
-
* @internal
|
|
107
|
-
*/
|
|
108
|
-
_loggerTypeName;
|
|
109
55
|
/**
|
|
110
56
|
* The type initialisers.
|
|
111
57
|
* @internal
|
|
@@ -144,8 +90,8 @@ class EngineCore {
|
|
|
144
90
|
this._skipBootstrap = options.skipBootstrap ?? false;
|
|
145
91
|
this._populateTypeInitialisers = options.populateTypeInitialisers;
|
|
146
92
|
this._customBootstrap = options.customBootstrap;
|
|
147
|
-
this._loggerTypeName = options.loggerTypeName ?? EngineCore.LOGGER_TYPE_NAME;
|
|
148
93
|
this._typeInitialisers = [];
|
|
94
|
+
this._contextIdKeys = [];
|
|
149
95
|
this._context = {
|
|
150
96
|
config: options.config,
|
|
151
97
|
registeredInstances: {},
|
|
@@ -163,20 +109,67 @@ class EngineCore {
|
|
|
163
109
|
/**
|
|
164
110
|
* Add a type initialiser.
|
|
165
111
|
* @param type The type to add the initialiser for.
|
|
166
|
-
* @param typeConfig The type config.
|
|
167
112
|
* @param module The name of the module which contains the initialiser method.
|
|
168
113
|
* @param method The name of the method to call.
|
|
169
114
|
*/
|
|
170
|
-
addTypeInitialiser(type,
|
|
171
|
-
|
|
115
|
+
addTypeInitialiser(type, module, method) {
|
|
116
|
+
Guards.stringValue(EngineCore.CLASS_NAME, "type", type);
|
|
117
|
+
Guards.stringValue(EngineCore.CLASS_NAME, "module", module);
|
|
118
|
+
Guards.stringValue(EngineCore.CLASS_NAME, "method", method);
|
|
119
|
+
const currentIndex = this._typeInitialisers.findIndex(t => t.type === type);
|
|
120
|
+
if (currentIndex >= 0) {
|
|
121
|
+
this._typeInitialisers[currentIndex].module = module;
|
|
122
|
+
this._typeInitialisers[currentIndex].method = method;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
172
125
|
this._typeInitialisers.push({
|
|
173
126
|
type,
|
|
174
|
-
typeConfig,
|
|
175
127
|
module,
|
|
176
128
|
method
|
|
177
129
|
});
|
|
178
130
|
}
|
|
179
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the type config for a specific type.
|
|
134
|
+
* @param type The type to get the config for.
|
|
135
|
+
* @returns The type config or undefined if not found.
|
|
136
|
+
*/
|
|
137
|
+
getTypeConfig(type) {
|
|
138
|
+
Guards.stringValue(EngineCore.CLASS_NAME, "type", type);
|
|
139
|
+
return this._context.config.types?.[type];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Add a context ID key to the engine.
|
|
143
|
+
* @param key The context ID key.
|
|
144
|
+
*/
|
|
145
|
+
addContextIdKey(key) {
|
|
146
|
+
if (!this._contextIdKeys.includes(key)) {
|
|
147
|
+
this._contextIdKeys.push(key);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get the context ID keys for the engine.
|
|
152
|
+
* @returns The context IDs keys.
|
|
153
|
+
*/
|
|
154
|
+
getContextIdKeys() {
|
|
155
|
+
return this._contextIdKeys;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Add a context ID to the engine.
|
|
159
|
+
* @param key The context ID key.
|
|
160
|
+
* @param value The context ID value.
|
|
161
|
+
*/
|
|
162
|
+
addContextId(key, value) {
|
|
163
|
+
this._contextIds ??= {};
|
|
164
|
+
this._contextIds[key] = value;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get the context IDs for the engine.
|
|
168
|
+
* @returns The context IDs or undefined if none are set.
|
|
169
|
+
*/
|
|
170
|
+
getContextIds() {
|
|
171
|
+
return this._contextIds;
|
|
172
|
+
}
|
|
180
173
|
/**
|
|
181
174
|
* Start the engine core.
|
|
182
175
|
* @returns True if the start was successful.
|
|
@@ -186,41 +179,60 @@ class EngineCore {
|
|
|
186
179
|
return false;
|
|
187
180
|
}
|
|
188
181
|
this.setupEngineLogger();
|
|
189
|
-
this.logInfo(I18n.formatMessage(`${
|
|
182
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.starting`));
|
|
190
183
|
if (this._context.config.debug) {
|
|
191
|
-
this.logInfo(I18n.formatMessage(`${
|
|
184
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.debuggingEnabled`));
|
|
192
185
|
}
|
|
193
186
|
let canContinue;
|
|
194
187
|
try {
|
|
195
188
|
canContinue = await this.stateLoad();
|
|
196
189
|
if (canContinue) {
|
|
197
|
-
for (const { type,
|
|
198
|
-
await this.initialiseTypeConfig(type,
|
|
190
|
+
for (const { type, module, method } of this._typeInitialisers) {
|
|
191
|
+
await this.initialiseTypeConfig(type, module, method);
|
|
199
192
|
}
|
|
200
193
|
await this.bootstrap();
|
|
201
|
-
this.logInfo(I18n.formatMessage(`${
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
194
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.componentsStarting`));
|
|
195
|
+
await ContextIdStore.run(this._contextIds ?? {}, async () => {
|
|
196
|
+
for (const instance of this._context.componentInstances) {
|
|
197
|
+
if (!instance.started) {
|
|
198
|
+
const startMethod = instance.component.start?.bind(instance.component);
|
|
199
|
+
if (Is.function(startMethod)) {
|
|
200
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.componentStarting`, {
|
|
201
|
+
className: instance.component.className(),
|
|
202
|
+
instanceType: instance.instanceType
|
|
203
|
+
}));
|
|
204
|
+
try {
|
|
205
|
+
await startMethod(EngineCore.LOGGING_COMPONENT_TYPE_NAME);
|
|
206
|
+
instance.started = true;
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
await this.logError(new GeneralError(EngineCore.CLASS_NAME, "componentStartFailed", {
|
|
210
|
+
className: instance.component.className(),
|
|
211
|
+
instanceType: instance.instanceType
|
|
212
|
+
}, BaseError.fromError(err)));
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
208
217
|
}
|
|
209
|
-
}
|
|
210
|
-
this.logInfo(I18n.formatMessage(`${
|
|
218
|
+
});
|
|
219
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.componentsComplete`));
|
|
211
220
|
}
|
|
212
|
-
this.logInfo(I18n.formatMessage(`${
|
|
221
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.started`));
|
|
213
222
|
this._isStarted = true;
|
|
214
223
|
}
|
|
215
224
|
catch (err) {
|
|
216
225
|
canContinue = false;
|
|
217
|
-
this.logError(BaseError.fromError(err));
|
|
226
|
+
await this.logError(BaseError.fromError(err));
|
|
218
227
|
}
|
|
219
228
|
finally {
|
|
220
229
|
if (!(await this.stateSave())) {
|
|
221
230
|
canContinue = false;
|
|
222
231
|
}
|
|
223
232
|
}
|
|
233
|
+
if (!canContinue) {
|
|
234
|
+
await this.stop();
|
|
235
|
+
}
|
|
224
236
|
return canContinue;
|
|
225
237
|
}
|
|
226
238
|
/**
|
|
@@ -228,26 +240,34 @@ class EngineCore {
|
|
|
228
240
|
* @returns Nothing.
|
|
229
241
|
*/
|
|
230
242
|
async stop() {
|
|
231
|
-
this.logInfo(I18n.formatMessage(`${
|
|
232
|
-
this.logInfo(I18n.formatMessage(`${
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.stopping`));
|
|
244
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.componentsStopping`));
|
|
245
|
+
await ContextIdStore.run(this._contextIds ?? {}, async () => {
|
|
246
|
+
for (const instance of this._context.componentInstances) {
|
|
247
|
+
if (instance.started) {
|
|
248
|
+
instance.started = false;
|
|
249
|
+
const stopMethod = instance.component.stop?.bind(instance.component);
|
|
250
|
+
if (Is.function(stopMethod)) {
|
|
251
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.componentStopping`, {
|
|
252
|
+
className: instance.component.className(),
|
|
253
|
+
instanceType: instance.instanceType
|
|
254
|
+
}));
|
|
255
|
+
try {
|
|
256
|
+
await stopMethod(EngineCore.LOGGING_COMPONENT_TYPE_NAME);
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
await this.logError(new GeneralError(EngineCore.CLASS_NAME, "componentStopFailed", {
|
|
260
|
+
className: instance.component.className(),
|
|
261
|
+
instanceType: instance.instanceType
|
|
262
|
+
}, BaseError.fromError(err)));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
245
265
|
}
|
|
246
266
|
}
|
|
247
|
-
}
|
|
267
|
+
});
|
|
248
268
|
await this.stateSave();
|
|
249
|
-
this.logInfo(I18n.formatMessage(`${
|
|
250
|
-
this.logInfo(I18n.formatMessage(`${
|
|
269
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.componentsStopped`));
|
|
270
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.stopped`));
|
|
251
271
|
}
|
|
252
272
|
/**
|
|
253
273
|
* Is the engine started.
|
|
@@ -274,9 +294,9 @@ class EngineCore {
|
|
|
274
294
|
* Log info.
|
|
275
295
|
* @param message The message to log.
|
|
276
296
|
*/
|
|
277
|
-
logInfo(message) {
|
|
278
|
-
this._engineLoggingComponent?.log({
|
|
279
|
-
source: EngineCore.
|
|
297
|
+
async logInfo(message) {
|
|
298
|
+
await this._engineLoggingComponent?.log({
|
|
299
|
+
source: EngineCore.CLASS_NAME,
|
|
280
300
|
level: "info",
|
|
281
301
|
message
|
|
282
302
|
});
|
|
@@ -285,7 +305,7 @@ class EngineCore {
|
|
|
285
305
|
* Log error.
|
|
286
306
|
* @param error The error to log.
|
|
287
307
|
*/
|
|
288
|
-
logError(error) {
|
|
308
|
+
async logError(error) {
|
|
289
309
|
const formattedErrors = ErrorHelper.localizeErrors(error);
|
|
290
310
|
for (const formattedError of formattedErrors) {
|
|
291
311
|
let message = Is.stringValue(formattedError.source)
|
|
@@ -294,8 +314,8 @@ class EngineCore {
|
|
|
294
314
|
if (this._context.config.debug && Is.stringValue(formattedError.stack)) {
|
|
295
315
|
message += `\n${formattedError.stack}`;
|
|
296
316
|
}
|
|
297
|
-
this._engineLoggingComponent?.log({
|
|
298
|
-
source: EngineCore.
|
|
317
|
+
await this._engineLoggingComponent?.log({
|
|
318
|
+
source: EngineCore.CLASS_NAME,
|
|
299
319
|
level: "error",
|
|
300
320
|
message
|
|
301
321
|
});
|
|
@@ -330,10 +350,10 @@ class EngineCore {
|
|
|
330
350
|
* @throws If a matching instance was not found.
|
|
331
351
|
*/
|
|
332
352
|
getRegisteredInstanceType(componentConnectorType, features) {
|
|
333
|
-
Guards.stringValue(EngineCore.
|
|
353
|
+
Guards.stringValue(EngineCore.CLASS_NAME, "componentConnectorType", componentConnectorType);
|
|
334
354
|
const registeredType = this.getRegisteredInstanceTypeOptional(componentConnectorType, features);
|
|
335
355
|
if (!Is.stringValue(registeredType)) {
|
|
336
|
-
throw new GeneralError(EngineCore.
|
|
356
|
+
throw new GeneralError(EngineCore.CLASS_NAME, "instanceTypeNotFound", {
|
|
337
357
|
type: componentConnectorType,
|
|
338
358
|
features: (features ?? ["default"]).join(",")
|
|
339
359
|
});
|
|
@@ -354,7 +374,12 @@ class EngineCore {
|
|
|
354
374
|
registeredType = registeredTypes.find(t => t.features?.every(f => features.includes(f)))?.type;
|
|
355
375
|
}
|
|
356
376
|
else {
|
|
357
|
-
|
|
377
|
+
// First look for the default entry
|
|
378
|
+
registeredType = registeredTypes.find(t => t.isDefault)?.type;
|
|
379
|
+
// Can't find a default so just use the first entry
|
|
380
|
+
if (!Is.stringValue(registeredType)) {
|
|
381
|
+
registeredType = registeredTypes[0]?.type;
|
|
382
|
+
}
|
|
358
383
|
}
|
|
359
384
|
}
|
|
360
385
|
return registeredType;
|
|
@@ -374,7 +399,7 @@ class EngineCore {
|
|
|
374
399
|
state: this._context.state,
|
|
375
400
|
typeInitialisers: this._typeInitialisers,
|
|
376
401
|
entitySchemas,
|
|
377
|
-
|
|
402
|
+
contextIdKeys: this._contextIdKeys
|
|
378
403
|
};
|
|
379
404
|
return cloneData;
|
|
380
405
|
}
|
|
@@ -384,11 +409,10 @@ class EngineCore {
|
|
|
384
409
|
* @param silent Should the clone be silent.
|
|
385
410
|
*/
|
|
386
411
|
populateClone(cloneData, silent) {
|
|
387
|
-
Guards.object(EngineCore.
|
|
388
|
-
Guards.object(EngineCore.
|
|
389
|
-
Guards.object(EngineCore.
|
|
390
|
-
Guards.array(EngineCore.
|
|
391
|
-
this._loggerTypeName = cloneData.loggerTypeName;
|
|
412
|
+
Guards.object(EngineCore.CLASS_NAME, "cloneData", cloneData);
|
|
413
|
+
Guards.object(EngineCore.CLASS_NAME, "cloneData.config", cloneData.config);
|
|
414
|
+
Guards.object(EngineCore.CLASS_NAME, "cloneData.state", cloneData.state);
|
|
415
|
+
Guards.array(EngineCore.CLASS_NAME, "cloneData.typeInitialisers", cloneData.typeInitialisers);
|
|
392
416
|
this._skipBootstrap = true;
|
|
393
417
|
this._isClone = true;
|
|
394
418
|
if (silent ?? false) {
|
|
@@ -402,6 +426,7 @@ class EngineCore {
|
|
|
402
426
|
stateDirty: false
|
|
403
427
|
};
|
|
404
428
|
this._typeInitialisers = cloneData.typeInitialisers;
|
|
429
|
+
this._contextIdKeys.push(...cloneData.contextIdKeys);
|
|
405
430
|
for (const schemaName of Object.keys(cloneData.entitySchemas)) {
|
|
406
431
|
EntitySchemaFactory.register(schemaName, () => cloneData.entitySchemas[schemaName]);
|
|
407
432
|
}
|
|
@@ -414,25 +439,36 @@ class EngineCore {
|
|
|
414
439
|
* @param instanceMethod The function to initialise the instance.
|
|
415
440
|
* @internal
|
|
416
441
|
*/
|
|
417
|
-
async initialiseTypeConfig(typeKey,
|
|
442
|
+
async initialiseTypeConfig(typeKey, module, method) {
|
|
443
|
+
const typeConfig = this._context.config.types?.[typeKey];
|
|
418
444
|
if (Is.arrayValue(typeConfig)) {
|
|
419
445
|
const instanceMethod = await ModuleHelper.getModuleEntry(module, method);
|
|
420
446
|
for (let i = 0; i < typeConfig.length; i++) {
|
|
421
|
-
|
|
422
|
-
|
|
447
|
+
await this.logInfo(I18n.formatMessage("engineCore.configuring", {
|
|
448
|
+
componentType: typeKey,
|
|
449
|
+
configType: typeConfig[i].type
|
|
450
|
+
}));
|
|
451
|
+
const result = await instanceMethod(this, this._context, typeConfig[i]);
|
|
452
|
+
if (Is.stringValue(result.instanceType) && Is.object(result.component)) {
|
|
453
|
+
const finalInstanceType = typeConfig[i].overrideInstanceType ?? result.instanceType;
|
|
454
|
+
this._context.componentInstances.push({
|
|
455
|
+
instanceType: finalInstanceType,
|
|
456
|
+
component: result.component,
|
|
457
|
+
started: false
|
|
458
|
+
});
|
|
459
|
+
result.factory?.register(finalInstanceType, () => result.component);
|
|
423
460
|
this._context.registeredInstances[typeKey] ??= [];
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
461
|
+
this._context.registeredInstances[typeKey].push({
|
|
462
|
+
type: finalInstanceType,
|
|
463
|
+
isDefault: typeConfig[i].isDefault,
|
|
464
|
+
features: typeConfig[i].features
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
throw new GeneralError("engineCore", "componentUnknownType", {
|
|
469
|
+
type: typeConfig[i].type,
|
|
470
|
+
componentType: typeKey
|
|
471
|
+
});
|
|
436
472
|
}
|
|
437
473
|
}
|
|
438
474
|
}
|
|
@@ -452,23 +488,24 @@ class EngineCore {
|
|
|
452
488
|
}
|
|
453
489
|
});
|
|
454
490
|
this._context.componentInstances.push({
|
|
455
|
-
instanceType:
|
|
456
|
-
component: engineLoggerConnector
|
|
491
|
+
instanceType: EngineCore.LOGGING_CONNECTOR_TYPE_NAME,
|
|
492
|
+
component: engineLoggerConnector,
|
|
493
|
+
started: false
|
|
457
494
|
});
|
|
458
|
-
LoggingConnectorFactory.register(
|
|
495
|
+
LoggingConnectorFactory.register(EngineCore.LOGGING_CONNECTOR_TYPE_NAME, () => engineLoggerConnector);
|
|
459
496
|
this._context.registeredInstances.loggingConnector = [
|
|
460
497
|
{
|
|
461
|
-
type:
|
|
498
|
+
type: EngineCore.LOGGING_CONNECTOR_TYPE_NAME
|
|
462
499
|
}
|
|
463
500
|
];
|
|
464
501
|
const engineLoggerComponent = new LoggingService({
|
|
465
|
-
loggingConnectorType:
|
|
502
|
+
loggingConnectorType: EngineCore.LOGGING_CONNECTOR_TYPE_NAME
|
|
466
503
|
});
|
|
467
504
|
this._engineLoggingComponent = engineLoggerComponent;
|
|
468
|
-
ComponentFactory.register(
|
|
505
|
+
ComponentFactory.register(EngineCore.LOGGING_COMPONENT_TYPE_NAME, () => engineLoggerComponent);
|
|
469
506
|
this._context.registeredInstances.loggingComponent = [
|
|
470
507
|
{
|
|
471
|
-
type:
|
|
508
|
+
type: EngineCore.LOGGING_COMPONENT_TYPE_NAME
|
|
472
509
|
}
|
|
473
510
|
];
|
|
474
511
|
}
|
|
@@ -485,7 +522,7 @@ class EngineCore {
|
|
|
485
522
|
return true;
|
|
486
523
|
}
|
|
487
524
|
catch (err) {
|
|
488
|
-
this.logError(BaseError.fromError(err));
|
|
525
|
+
await this.logError(BaseError.fromError(err));
|
|
489
526
|
return false;
|
|
490
527
|
}
|
|
491
528
|
}
|
|
@@ -504,7 +541,7 @@ class EngineCore {
|
|
|
504
541
|
return true;
|
|
505
542
|
}
|
|
506
543
|
catch (err) {
|
|
507
|
-
this.logError(BaseError.fromError(err));
|
|
544
|
+
await this.logError(BaseError.fromError(err));
|
|
508
545
|
}
|
|
509
546
|
return false;
|
|
510
547
|
}
|
|
@@ -516,130 +553,32 @@ class EngineCore {
|
|
|
516
553
|
*/
|
|
517
554
|
async bootstrap() {
|
|
518
555
|
if (!this._skipBootstrap) {
|
|
519
|
-
this.logInfo(I18n.formatMessage(`${
|
|
556
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.bootstrapStarted`));
|
|
520
557
|
// First bootstrap the components.
|
|
521
558
|
for (const instance of this._context.componentInstances) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
this.logInfo(I18n.formatMessage(`${
|
|
525
|
-
|
|
559
|
+
const bootstrapMethod = instance.component.bootstrap?.bind(instance.component);
|
|
560
|
+
if (Is.function(bootstrapMethod)) {
|
|
561
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.bootstrapping`, {
|
|
562
|
+
className: instance.component.className(),
|
|
563
|
+
instanceType: instance.instanceType
|
|
526
564
|
}));
|
|
527
|
-
const bootstrapSuccess = await
|
|
565
|
+
const bootstrapSuccess = await bootstrapMethod(EngineCore.LOGGING_COMPONENT_TYPE_NAME);
|
|
528
566
|
// If the bootstrap method failed then throw an error
|
|
529
567
|
if (!bootstrapSuccess) {
|
|
530
|
-
throw new GeneralError(EngineCore.
|
|
531
|
-
|
|
568
|
+
throw new GeneralError(EngineCore.CLASS_NAME, "bootstrapFailed", {
|
|
569
|
+
className: instance.component.className(),
|
|
570
|
+
instanceType: instance.instanceType
|
|
532
571
|
});
|
|
533
572
|
}
|
|
534
573
|
}
|
|
535
574
|
}
|
|
536
575
|
// Now perform any custom bootstrap operations
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapComplete`));
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Get the instance name.
|
|
545
|
-
* @param instance The instance to get the name for.
|
|
546
|
-
* @param instance.instanceType The instance type.
|
|
547
|
-
* @param instance.component The component.
|
|
548
|
-
* @returns The instance name.
|
|
549
|
-
* @internal
|
|
550
|
-
*/
|
|
551
|
-
getInstanceName(instance) {
|
|
552
|
-
return `${instance.component.CLASS_NAME}-${instance.instanceType}`;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Copyright 2024 IOTA Stiftung.
|
|
557
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
558
|
-
/**
|
|
559
|
-
* Store state in a file.
|
|
560
|
-
*/
|
|
561
|
-
class FileStateStorage {
|
|
562
|
-
/**
|
|
563
|
-
* Runtime name for the class.
|
|
564
|
-
*/
|
|
565
|
-
CLASS_NAME = "FileStateStorage";
|
|
566
|
-
/**
|
|
567
|
-
* The filename to store the state.
|
|
568
|
-
* @internal
|
|
569
|
-
*/
|
|
570
|
-
_filename;
|
|
571
|
-
/**
|
|
572
|
-
* Readonly mode state file is not updated.
|
|
573
|
-
* @internal
|
|
574
|
-
*/
|
|
575
|
-
_readonlyMode;
|
|
576
|
-
/**
|
|
577
|
-
* Create a new instance of FileStateStorage.
|
|
578
|
-
* @param filename The filename to store the state.
|
|
579
|
-
* @param readonlyMode Whether the file is in read-only mode.
|
|
580
|
-
*/
|
|
581
|
-
constructor(filename, readonlyMode = false) {
|
|
582
|
-
Guards.stringValue(this.CLASS_NAME, "filename", filename);
|
|
583
|
-
this._filename = filename;
|
|
584
|
-
this._readonlyMode = readonlyMode;
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* Method for loading the state.
|
|
588
|
-
* @param engineCore The engine core to load the state for.
|
|
589
|
-
* @returns The state of the engine or undefined if it doesn't exist.
|
|
590
|
-
*/
|
|
591
|
-
async load(engineCore) {
|
|
592
|
-
try {
|
|
593
|
-
engineCore.logInfo(I18n.formatMessage(`${StringHelper.camelCase(this.CLASS_NAME)}.loading`, {
|
|
594
|
-
filename: this._filename
|
|
595
|
-
}));
|
|
596
|
-
if (await this.fileExists(this._filename)) {
|
|
597
|
-
const content = await readFile(this._filename, "utf8");
|
|
598
|
-
return JSON.parse(content.toString());
|
|
576
|
+
const customBootstrap = this._customBootstrap;
|
|
577
|
+
if (Is.function(customBootstrap)) {
|
|
578
|
+
await customBootstrap.call(this, this, this._context);
|
|
599
579
|
}
|
|
600
|
-
|
|
601
|
-
catch (err) {
|
|
602
|
-
throw new GeneralError(this.CLASS_NAME, "loadingError", { filename: this._filename }, BaseError.fromError(err));
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Method for saving the state.
|
|
607
|
-
* @param engineCore The engine core to save the state for.
|
|
608
|
-
* @param state The state of the engine to save.
|
|
609
|
-
* @returns Nothing.
|
|
610
|
-
*/
|
|
611
|
-
async save(engineCore, state) {
|
|
612
|
-
if (!this._readonlyMode) {
|
|
613
|
-
try {
|
|
614
|
-
engineCore.logInfo(I18n.formatMessage(`${StringHelper.camelCase(this.CLASS_NAME)}.saving`, {
|
|
615
|
-
filename: this._filename
|
|
616
|
-
}));
|
|
617
|
-
try {
|
|
618
|
-
await mkdir(path.dirname(this._filename), { recursive: true });
|
|
619
|
-
}
|
|
620
|
-
catch { }
|
|
621
|
-
await writeFile(this._filename, JSON.stringify(state, undefined, "\t"), "utf8");
|
|
622
|
-
}
|
|
623
|
-
catch (err) {
|
|
624
|
-
throw new GeneralError(this.CLASS_NAME, "savingError", { filename: this._filename }, BaseError.fromError(err));
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
|
-
* Does the specified file exist.
|
|
630
|
-
* @param filename The filename to check for existence.
|
|
631
|
-
* @returns True if the file exists.
|
|
632
|
-
* @internal
|
|
633
|
-
*/
|
|
634
|
-
async fileExists(filename) {
|
|
635
|
-
try {
|
|
636
|
-
const stats = await stat(filename);
|
|
637
|
-
return stats.isFile();
|
|
638
|
-
}
|
|
639
|
-
catch {
|
|
640
|
-
return false;
|
|
580
|
+
await this.logInfo(I18n.formatMessage(`${"engineCore"}.bootstrapComplete`));
|
|
641
581
|
}
|
|
642
582
|
}
|
|
643
583
|
}
|
|
644
|
-
|
|
645
|
-
export { EngineCore, FileStateStorage, MemoryStateStorage };
|
|
584
|
+
//# sourceMappingURL=engineCore.js.map
|