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