@twin.org/engine-core 0.0.2-next.4 → 0.0.2-next.6

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.
@@ -73,10 +73,16 @@ class EngineCore {
73
73
  * Name for the engine logger.
74
74
  */
75
75
  static LOGGER_TYPE_NAME = "engine";
76
+ /**
77
+ * Runtime name for the class in camel case.
78
+ * @internal
79
+ */
80
+ static _CLASS_NAME_CAMEL_CASE = core.StringHelper.camelCase("EngineCore");
76
81
  /**
77
82
  * Runtime name for the class.
83
+ * @internal
78
84
  */
79
- CLASS_NAME = "EngineCore";
85
+ static _CLASS_NAME = "EngineCore";
80
86
  /**
81
87
  * The core context.
82
88
  */
@@ -87,10 +93,10 @@ class EngineCore {
87
93
  */
88
94
  _stateStorage;
89
95
  /**
90
- * The logging connector for the engine.
96
+ * The logging component for the engine.
91
97
  * @internal
92
98
  */
93
- _engineLoggingConnector;
99
+ _engineLoggingComponent;
94
100
  /**
95
101
  * Skip the bootstrap process.
96
102
  * @internal
@@ -138,7 +144,7 @@ class EngineCore {
138
144
  this._typeInitialisers = [];
139
145
  this._context = {
140
146
  config: options.config,
141
- defaultTypes: {},
147
+ registeredInstances: {},
142
148
  componentInstances: [],
143
149
  state: { componentStates: {} },
144
150
  stateDirty: false
@@ -175,9 +181,9 @@ class EngineCore {
175
181
  return false;
176
182
  }
177
183
  this.setupEngineLogger();
178
- this.logInfo(core.I18n.formatMessage("engineCore.starting"));
184
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.starting`));
179
185
  if (this._context.config.debug) {
180
- this.logInfo(core.I18n.formatMessage("engineCore.debuggingEnabled"));
186
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.debuggingEnabled`));
181
187
  }
182
188
  let canContinue;
183
189
  try {
@@ -187,11 +193,11 @@ class EngineCore {
187
193
  await this.initialiseTypeConfig(type, typeConfig, module, method);
188
194
  }
189
195
  await this.bootstrap();
190
- this.logInfo(core.I18n.formatMessage("engineCore.componentsStarting"));
196
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsStarting`));
191
197
  for (const instance of this._context.componentInstances) {
192
198
  if (core.Is.function(instance.component.start)) {
193
199
  const instanceName = this.getInstanceName(instance);
194
- this.logInfo(core.I18n.formatMessage("engineCore.componentStarting", {
200
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentStarting`, {
195
201
  element: instance.instanceType
196
202
  }));
197
203
  const componentState = this._context.state.componentStates[instanceName] ?? {};
@@ -203,9 +209,9 @@ class EngineCore {
203
209
  }
204
210
  }
205
211
  }
206
- this.logInfo(core.I18n.formatMessage("engineCore.componentsComplete"));
212
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsComplete`));
207
213
  }
208
- this.logInfo(core.I18n.formatMessage("engineCore.started"));
214
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.started`));
209
215
  this._isStarted = true;
210
216
  }
211
217
  catch (err) {
@@ -224,14 +230,16 @@ class EngineCore {
224
230
  * @returns Nothing.
225
231
  */
226
232
  async stop() {
227
- this.logInfo(core.I18n.formatMessage("engineCore.stopping"));
228
- this.logInfo(core.I18n.formatMessage("engineCore.componentsStopping"));
233
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.stopping`));
234
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsStopping`));
229
235
  for (const instance of this._context.componentInstances) {
230
236
  if (core.Is.function(instance.component.stop)) {
231
237
  const instanceName = this.getInstanceName(instance);
232
238
  const componentState = this._context.state.componentStates[instanceName] ?? {};
233
239
  const lastState = core.ObjectHelper.clone(componentState);
234
- this.logInfo(core.I18n.formatMessage("engineCore.componentStopping", { element: instance.instanceType }));
240
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentStopping`, {
241
+ element: instance.instanceType
242
+ }));
235
243
  try {
236
244
  await instance.component.stop(this._context.state.nodeIdentity, this._loggerTypeName, componentState);
237
245
  if (!core.ObjectHelper.equal(lastState, componentState)) {
@@ -240,23 +248,23 @@ class EngineCore {
240
248
  }
241
249
  }
242
250
  catch (err) {
243
- this.logError(new core.GeneralError(this.CLASS_NAME, "componentStopFailed", {
251
+ this.logError(new core.GeneralError(EngineCore._CLASS_NAME, "componentStopFailed", {
244
252
  component: instance.instanceType
245
253
  }, core.BaseError.fromError(err)));
246
254
  }
247
255
  }
248
256
  }
249
257
  await this.stateSave();
250
- this.logInfo(core.I18n.formatMessage("engineCore.componentsStopped"));
251
- this.logInfo(core.I18n.formatMessage("engineCore.stopped"));
258
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsStopped`));
259
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.stopped`));
252
260
  }
253
261
  /**
254
262
  * Log info.
255
263
  * @param message The message to log.
256
264
  */
257
265
  logInfo(message) {
258
- this._engineLoggingConnector?.log({
259
- source: this.CLASS_NAME,
266
+ this._engineLoggingComponent?.log({
267
+ source: EngineCore._CLASS_NAME,
260
268
  level: "info",
261
269
  message
262
270
  });
@@ -274,8 +282,8 @@ class EngineCore {
274
282
  if (this._context.config.debug && core.Is.stringValue(formattedError.stack)) {
275
283
  message += `\n${formattedError.stack}`;
276
284
  }
277
- this._engineLoggingConnector?.log({
278
- source: this.CLASS_NAME,
285
+ this._engineLoggingComponent?.log({
286
+ source: EngineCore._CLASS_NAME,
279
287
  level: "error",
280
288
  message
281
289
  });
@@ -296,11 +304,48 @@ class EngineCore {
296
304
  return this._context.state;
297
305
  }
298
306
  /**
299
- * Get the types for the component.
300
- * @returns The default types.
307
+ * Get all the registered instances.
308
+ * @returns The registered instances.
301
309
  */
302
- getDefaultTypes() {
303
- return this._context.defaultTypes;
310
+ getRegisteredInstances() {
311
+ return this._context.registeredInstances;
312
+ }
313
+ /**
314
+ * Get the registered instance type for the component/connector.
315
+ * @param componentConnectorType The type of the component/connector.
316
+ * @param features The requested features of the component, if not specified the default entry will be retrieved.
317
+ * @returns The instance type matching the criteria if one is registered.
318
+ * @throws If a matching instance was not found.
319
+ */
320
+ getRegisteredInstanceType(componentConnectorType, features) {
321
+ core.Guards.stringValue(EngineCore._CLASS_NAME, "componentConnectorType", componentConnectorType);
322
+ const registeredType = this.getRegisteredInstanceTypeOptional(componentConnectorType, features);
323
+ if (!core.Is.stringValue(registeredType)) {
324
+ throw new core.GeneralError(EngineCore._CLASS_NAME, "instanceTypeNotFound", {
325
+ type: componentConnectorType,
326
+ features: (features ?? ["default"]).join(",")
327
+ });
328
+ }
329
+ return registeredType;
330
+ }
331
+ /**
332
+ * Get the registered instance type for the component/connector if it exists.
333
+ * @param componentConnectorType The type of the component/connector.
334
+ * @param features The requested features of the component, if not specified the default entry will be retrieved.
335
+ * @returns The instance type matching the criteria if one is registered.
336
+ */
337
+ getRegisteredInstanceTypeOptional(componentConnectorType, features) {
338
+ let registeredType;
339
+ const registeredTypes = this._context.registeredInstances[componentConnectorType];
340
+ if (core.Is.arrayValue(registeredTypes)) {
341
+ if (core.Is.arrayValue(features)) {
342
+ registeredType = registeredTypes.find(t => t.features?.every(f => features.includes(f)))?.type;
343
+ }
344
+ else {
345
+ registeredType = registeredTypes[0]?.type;
346
+ }
347
+ }
348
+ return registeredType;
304
349
  }
305
350
  /**
306
351
  * Get the data required to create a clone of the engine.
@@ -327,10 +372,10 @@ class EngineCore {
327
372
  * @param silent Should the clone be silent.
328
373
  */
329
374
  populateClone(cloneData, silent) {
330
- core.Guards.object(this.CLASS_NAME, "cloneData", cloneData);
331
- core.Guards.object(this.CLASS_NAME, "cloneData.config", cloneData.config);
332
- core.Guards.object(this.CLASS_NAME, "cloneData.state", cloneData.state);
333
- core.Guards.array(this.CLASS_NAME, "cloneData.typeInitialisers", cloneData.typeInitialisers);
375
+ core.Guards.object(EngineCore._CLASS_NAME, "cloneData", cloneData);
376
+ core.Guards.object(EngineCore._CLASS_NAME, "cloneData.config", cloneData.config);
377
+ core.Guards.object(EngineCore._CLASS_NAME, "cloneData.state", cloneData.state);
378
+ core.Guards.array(EngineCore._CLASS_NAME, "cloneData.typeInitialisers", cloneData.typeInitialisers);
334
379
  this._loggerTypeName = cloneData.loggerTypeName;
335
380
  this._skipBootstrap = true;
336
381
  if (silent ?? false) {
@@ -338,7 +383,7 @@ class EngineCore {
338
383
  }
339
384
  this._context = {
340
385
  config: cloneData.config,
341
- defaultTypes: {},
386
+ registeredInstances: {},
342
387
  componentInstances: [],
343
388
  state: { componentStates: {} },
344
389
  stateDirty: false
@@ -361,9 +406,20 @@ class EngineCore {
361
406
  const instanceMethod = await modules.ModuleHelper.getModuleEntry(module, method);
362
407
  for (let i = 0; i < typeConfig.length; i++) {
363
408
  const instanceType = instanceMethod(this, this._context, typeConfig[i], typeConfig[i].overrideInstanceType);
364
- if (core.Is.stringValue(instanceType) &&
365
- (core.Is.empty(this._context.defaultTypes[typeKey]) || typeConfig[i].isDefault)) {
366
- this._context.defaultTypes[typeKey] = instanceType;
409
+ if (core.Is.stringValue(instanceType)) {
410
+ this._context.registeredInstances[typeKey] ??= [];
411
+ if (typeConfig[i].isDefault ?? false) {
412
+ this._context.registeredInstances[typeKey].unshift({
413
+ type: instanceType,
414
+ features: typeConfig[i].features
415
+ });
416
+ }
417
+ else {
418
+ this._context.registeredInstances[typeKey].push({
419
+ type: instanceType,
420
+ features: typeConfig[i].features
421
+ });
422
+ }
367
423
  }
368
424
  }
369
425
  }
@@ -387,13 +443,21 @@ class EngineCore {
387
443
  component: engineLoggerConnector
388
444
  });
389
445
  loggingModels.LoggingConnectorFactory.register(this._loggerTypeName, () => engineLoggerConnector);
390
- this._engineLoggingConnector = engineLoggerConnector;
391
- this._context.defaultTypes.loggingConnector = this._loggerTypeName;
446
+ this._context.registeredInstances.loggingConnector = [
447
+ {
448
+ type: this._loggerTypeName
449
+ }
450
+ ];
392
451
  const engineLoggerComponent = new loggingService.LoggingService({
393
452
  loggingConnectorType: this._loggerTypeName
394
453
  });
454
+ this._engineLoggingComponent = engineLoggerComponent;
395
455
  core.ComponentFactory.register("logging-service", () => engineLoggerComponent);
396
- this._context.defaultTypes.loggingComponent = "logging-service";
456
+ this._context.registeredInstances.loggingComponent = [
457
+ {
458
+ type: "logging-service"
459
+ }
460
+ ];
397
461
  }
398
462
  /**
399
463
  * Load the state.
@@ -442,12 +506,12 @@ class EngineCore {
442
506
  */
443
507
  async bootstrap() {
444
508
  if (!this._skipBootstrap) {
445
- this.logInfo(core.I18n.formatMessage("engineCore.bootstrapStarted"));
509
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapStarted`));
446
510
  // First bootstrap the components.
447
511
  for (const instance of this._context.componentInstances) {
448
512
  if (core.Is.function(instance.component.bootstrap)) {
449
513
  const instanceName = this.getInstanceName(instance);
450
- this.logInfo(core.I18n.formatMessage("engineCore.bootstrapping", {
514
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapping`, {
451
515
  element: instanceName
452
516
  }));
453
517
  const componentState = this._context.state.componentStates[instanceName] ?? {};
@@ -455,7 +519,7 @@ class EngineCore {
455
519
  const bootstrapSuccess = await instance.component.bootstrap(this._loggerTypeName, componentState);
456
520
  // If the bootstrap method failed then throw an error
457
521
  if (!bootstrapSuccess) {
458
- throw new core.GeneralError(this.CLASS_NAME, "bootstrapFailed", {
522
+ throw new core.GeneralError(EngineCore._CLASS_NAME, "bootstrapFailed", {
459
523
  component: `${instance.component.CLASS_NAME}:${instance.instanceType}`
460
524
  });
461
525
  }
@@ -469,7 +533,7 @@ class EngineCore {
469
533
  if (core.Is.function(this._customBootstrap)) {
470
534
  await this._customBootstrap(this, this._context);
471
535
  }
472
- this.logInfo(core.I18n.formatMessage("engineCore.bootstrapComplete"));
536
+ this.logInfo(core.I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapComplete`));
473
537
  }
474
538
  }
475
539
  /**
@@ -71,10 +71,16 @@ class EngineCore {
71
71
  * Name for the engine logger.
72
72
  */
73
73
  static LOGGER_TYPE_NAME = "engine";
74
+ /**
75
+ * Runtime name for the class in camel case.
76
+ * @internal
77
+ */
78
+ static _CLASS_NAME_CAMEL_CASE = StringHelper.camelCase("EngineCore");
74
79
  /**
75
80
  * Runtime name for the class.
81
+ * @internal
76
82
  */
77
- CLASS_NAME = "EngineCore";
83
+ static _CLASS_NAME = "EngineCore";
78
84
  /**
79
85
  * The core context.
80
86
  */
@@ -85,10 +91,10 @@ class EngineCore {
85
91
  */
86
92
  _stateStorage;
87
93
  /**
88
- * The logging connector for the engine.
94
+ * The logging component for the engine.
89
95
  * @internal
90
96
  */
91
- _engineLoggingConnector;
97
+ _engineLoggingComponent;
92
98
  /**
93
99
  * Skip the bootstrap process.
94
100
  * @internal
@@ -136,7 +142,7 @@ class EngineCore {
136
142
  this._typeInitialisers = [];
137
143
  this._context = {
138
144
  config: options.config,
139
- defaultTypes: {},
145
+ registeredInstances: {},
140
146
  componentInstances: [],
141
147
  state: { componentStates: {} },
142
148
  stateDirty: false
@@ -173,9 +179,9 @@ class EngineCore {
173
179
  return false;
174
180
  }
175
181
  this.setupEngineLogger();
176
- this.logInfo(I18n.formatMessage("engineCore.starting"));
182
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.starting`));
177
183
  if (this._context.config.debug) {
178
- this.logInfo(I18n.formatMessage("engineCore.debuggingEnabled"));
184
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.debuggingEnabled`));
179
185
  }
180
186
  let canContinue;
181
187
  try {
@@ -185,11 +191,11 @@ class EngineCore {
185
191
  await this.initialiseTypeConfig(type, typeConfig, module, method);
186
192
  }
187
193
  await this.bootstrap();
188
- this.logInfo(I18n.formatMessage("engineCore.componentsStarting"));
194
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsStarting`));
189
195
  for (const instance of this._context.componentInstances) {
190
196
  if (Is.function(instance.component.start)) {
191
197
  const instanceName = this.getInstanceName(instance);
192
- this.logInfo(I18n.formatMessage("engineCore.componentStarting", {
198
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentStarting`, {
193
199
  element: instance.instanceType
194
200
  }));
195
201
  const componentState = this._context.state.componentStates[instanceName] ?? {};
@@ -201,9 +207,9 @@ class EngineCore {
201
207
  }
202
208
  }
203
209
  }
204
- this.logInfo(I18n.formatMessage("engineCore.componentsComplete"));
210
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsComplete`));
205
211
  }
206
- this.logInfo(I18n.formatMessage("engineCore.started"));
212
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.started`));
207
213
  this._isStarted = true;
208
214
  }
209
215
  catch (err) {
@@ -222,14 +228,16 @@ class EngineCore {
222
228
  * @returns Nothing.
223
229
  */
224
230
  async stop() {
225
- this.logInfo(I18n.formatMessage("engineCore.stopping"));
226
- this.logInfo(I18n.formatMessage("engineCore.componentsStopping"));
231
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.stopping`));
232
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsStopping`));
227
233
  for (const instance of this._context.componentInstances) {
228
234
  if (Is.function(instance.component.stop)) {
229
235
  const instanceName = this.getInstanceName(instance);
230
236
  const componentState = this._context.state.componentStates[instanceName] ?? {};
231
237
  const lastState = ObjectHelper.clone(componentState);
232
- this.logInfo(I18n.formatMessage("engineCore.componentStopping", { element: instance.instanceType }));
238
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentStopping`, {
239
+ element: instance.instanceType
240
+ }));
233
241
  try {
234
242
  await instance.component.stop(this._context.state.nodeIdentity, this._loggerTypeName, componentState);
235
243
  if (!ObjectHelper.equal(lastState, componentState)) {
@@ -238,23 +246,23 @@ class EngineCore {
238
246
  }
239
247
  }
240
248
  catch (err) {
241
- this.logError(new GeneralError(this.CLASS_NAME, "componentStopFailed", {
249
+ this.logError(new GeneralError(EngineCore._CLASS_NAME, "componentStopFailed", {
242
250
  component: instance.instanceType
243
251
  }, BaseError.fromError(err)));
244
252
  }
245
253
  }
246
254
  }
247
255
  await this.stateSave();
248
- this.logInfo(I18n.formatMessage("engineCore.componentsStopped"));
249
- this.logInfo(I18n.formatMessage("engineCore.stopped"));
256
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.componentsStopped`));
257
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.stopped`));
250
258
  }
251
259
  /**
252
260
  * Log info.
253
261
  * @param message The message to log.
254
262
  */
255
263
  logInfo(message) {
256
- this._engineLoggingConnector?.log({
257
- source: this.CLASS_NAME,
264
+ this._engineLoggingComponent?.log({
265
+ source: EngineCore._CLASS_NAME,
258
266
  level: "info",
259
267
  message
260
268
  });
@@ -272,8 +280,8 @@ class EngineCore {
272
280
  if (this._context.config.debug && Is.stringValue(formattedError.stack)) {
273
281
  message += `\n${formattedError.stack}`;
274
282
  }
275
- this._engineLoggingConnector?.log({
276
- source: this.CLASS_NAME,
283
+ this._engineLoggingComponent?.log({
284
+ source: EngineCore._CLASS_NAME,
277
285
  level: "error",
278
286
  message
279
287
  });
@@ -294,11 +302,48 @@ class EngineCore {
294
302
  return this._context.state;
295
303
  }
296
304
  /**
297
- * Get the types for the component.
298
- * @returns The default types.
305
+ * Get all the registered instances.
306
+ * @returns The registered instances.
299
307
  */
300
- getDefaultTypes() {
301
- return this._context.defaultTypes;
308
+ getRegisteredInstances() {
309
+ return this._context.registeredInstances;
310
+ }
311
+ /**
312
+ * Get the registered instance type for the component/connector.
313
+ * @param componentConnectorType The type of the component/connector.
314
+ * @param features The requested features of the component, if not specified the default entry will be retrieved.
315
+ * @returns The instance type matching the criteria if one is registered.
316
+ * @throws If a matching instance was not found.
317
+ */
318
+ getRegisteredInstanceType(componentConnectorType, features) {
319
+ Guards.stringValue(EngineCore._CLASS_NAME, "componentConnectorType", componentConnectorType);
320
+ const registeredType = this.getRegisteredInstanceTypeOptional(componentConnectorType, features);
321
+ if (!Is.stringValue(registeredType)) {
322
+ throw new GeneralError(EngineCore._CLASS_NAME, "instanceTypeNotFound", {
323
+ type: componentConnectorType,
324
+ features: (features ?? ["default"]).join(",")
325
+ });
326
+ }
327
+ return registeredType;
328
+ }
329
+ /**
330
+ * Get the registered instance type for the component/connector if it exists.
331
+ * @param componentConnectorType The type of the component/connector.
332
+ * @param features The requested features of the component, if not specified the default entry will be retrieved.
333
+ * @returns The instance type matching the criteria if one is registered.
334
+ */
335
+ getRegisteredInstanceTypeOptional(componentConnectorType, features) {
336
+ let registeredType;
337
+ const registeredTypes = this._context.registeredInstances[componentConnectorType];
338
+ if (Is.arrayValue(registeredTypes)) {
339
+ if (Is.arrayValue(features)) {
340
+ registeredType = registeredTypes.find(t => t.features?.every(f => features.includes(f)))?.type;
341
+ }
342
+ else {
343
+ registeredType = registeredTypes[0]?.type;
344
+ }
345
+ }
346
+ return registeredType;
302
347
  }
303
348
  /**
304
349
  * Get the data required to create a clone of the engine.
@@ -325,10 +370,10 @@ class EngineCore {
325
370
  * @param silent Should the clone be silent.
326
371
  */
327
372
  populateClone(cloneData, silent) {
328
- Guards.object(this.CLASS_NAME, "cloneData", cloneData);
329
- Guards.object(this.CLASS_NAME, "cloneData.config", cloneData.config);
330
- Guards.object(this.CLASS_NAME, "cloneData.state", cloneData.state);
331
- Guards.array(this.CLASS_NAME, "cloneData.typeInitialisers", cloneData.typeInitialisers);
373
+ Guards.object(EngineCore._CLASS_NAME, "cloneData", cloneData);
374
+ Guards.object(EngineCore._CLASS_NAME, "cloneData.config", cloneData.config);
375
+ Guards.object(EngineCore._CLASS_NAME, "cloneData.state", cloneData.state);
376
+ Guards.array(EngineCore._CLASS_NAME, "cloneData.typeInitialisers", cloneData.typeInitialisers);
332
377
  this._loggerTypeName = cloneData.loggerTypeName;
333
378
  this._skipBootstrap = true;
334
379
  if (silent ?? false) {
@@ -336,7 +381,7 @@ class EngineCore {
336
381
  }
337
382
  this._context = {
338
383
  config: cloneData.config,
339
- defaultTypes: {},
384
+ registeredInstances: {},
340
385
  componentInstances: [],
341
386
  state: { componentStates: {} },
342
387
  stateDirty: false
@@ -359,9 +404,20 @@ class EngineCore {
359
404
  const instanceMethod = await ModuleHelper.getModuleEntry(module, method);
360
405
  for (let i = 0; i < typeConfig.length; i++) {
361
406
  const instanceType = instanceMethod(this, this._context, typeConfig[i], typeConfig[i].overrideInstanceType);
362
- if (Is.stringValue(instanceType) &&
363
- (Is.empty(this._context.defaultTypes[typeKey]) || typeConfig[i].isDefault)) {
364
- this._context.defaultTypes[typeKey] = instanceType;
407
+ if (Is.stringValue(instanceType)) {
408
+ this._context.registeredInstances[typeKey] ??= [];
409
+ if (typeConfig[i].isDefault ?? false) {
410
+ this._context.registeredInstances[typeKey].unshift({
411
+ type: instanceType,
412
+ features: typeConfig[i].features
413
+ });
414
+ }
415
+ else {
416
+ this._context.registeredInstances[typeKey].push({
417
+ type: instanceType,
418
+ features: typeConfig[i].features
419
+ });
420
+ }
365
421
  }
366
422
  }
367
423
  }
@@ -385,13 +441,21 @@ class EngineCore {
385
441
  component: engineLoggerConnector
386
442
  });
387
443
  LoggingConnectorFactory.register(this._loggerTypeName, () => engineLoggerConnector);
388
- this._engineLoggingConnector = engineLoggerConnector;
389
- this._context.defaultTypes.loggingConnector = this._loggerTypeName;
444
+ this._context.registeredInstances.loggingConnector = [
445
+ {
446
+ type: this._loggerTypeName
447
+ }
448
+ ];
390
449
  const engineLoggerComponent = new LoggingService({
391
450
  loggingConnectorType: this._loggerTypeName
392
451
  });
452
+ this._engineLoggingComponent = engineLoggerComponent;
393
453
  ComponentFactory.register("logging-service", () => engineLoggerComponent);
394
- this._context.defaultTypes.loggingComponent = "logging-service";
454
+ this._context.registeredInstances.loggingComponent = [
455
+ {
456
+ type: "logging-service"
457
+ }
458
+ ];
395
459
  }
396
460
  /**
397
461
  * Load the state.
@@ -440,12 +504,12 @@ class EngineCore {
440
504
  */
441
505
  async bootstrap() {
442
506
  if (!this._skipBootstrap) {
443
- this.logInfo(I18n.formatMessage("engineCore.bootstrapStarted"));
507
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapStarted`));
444
508
  // First bootstrap the components.
445
509
  for (const instance of this._context.componentInstances) {
446
510
  if (Is.function(instance.component.bootstrap)) {
447
511
  const instanceName = this.getInstanceName(instance);
448
- this.logInfo(I18n.formatMessage("engineCore.bootstrapping", {
512
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapping`, {
449
513
  element: instanceName
450
514
  }));
451
515
  const componentState = this._context.state.componentStates[instanceName] ?? {};
@@ -453,7 +517,7 @@ class EngineCore {
453
517
  const bootstrapSuccess = await instance.component.bootstrap(this._loggerTypeName, componentState);
454
518
  // If the bootstrap method failed then throw an error
455
519
  if (!bootstrapSuccess) {
456
- throw new GeneralError(this.CLASS_NAME, "bootstrapFailed", {
520
+ throw new GeneralError(EngineCore._CLASS_NAME, "bootstrapFailed", {
457
521
  component: `${instance.component.CLASS_NAME}:${instance.instanceType}`
458
522
  });
459
523
  }
@@ -467,7 +531,7 @@ class EngineCore {
467
531
  if (Is.function(this._customBootstrap)) {
468
532
  await this._customBootstrap(this, this._context);
469
533
  }
470
- this.logInfo(I18n.formatMessage("engineCore.bootstrapComplete"));
534
+ this.logInfo(I18n.formatMessage(`${EngineCore._CLASS_NAME_CAMEL_CASE}.bootstrapComplete`));
471
535
  }
472
536
  }
473
537
  /**
@@ -9,10 +9,6 @@ export declare class EngineCore<C extends IEngineCoreConfig = IEngineCoreConfig,
9
9
  * Name for the engine logger.
10
10
  */
11
11
  static readonly LOGGER_TYPE_NAME: string;
12
- /**
13
- * Runtime name for the class.
14
- */
15
- readonly CLASS_NAME: string;
16
12
  /**
17
13
  * The core context.
18
14
  */
@@ -61,12 +57,30 @@ export declare class EngineCore<C extends IEngineCoreConfig = IEngineCoreConfig,
61
57
  */
62
58
  getState(): S;
63
59
  /**
64
- * Get the types for the component.
65
- * @returns The default types.
60
+ * Get all the registered instances.
61
+ * @returns The registered instances.
66
62
  */
67
- getDefaultTypes(): {
68
- [type: string]: string;
63
+ getRegisteredInstances(): {
64
+ [name: string]: {
65
+ type: string;
66
+ features?: string[];
67
+ }[];
69
68
  };
69
+ /**
70
+ * Get the registered instance type for the component/connector.
71
+ * @param componentConnectorType The type of the component/connector.
72
+ * @param features The requested features of the component, if not specified the default entry will be retrieved.
73
+ * @returns The instance type matching the criteria if one is registered.
74
+ * @throws If a matching instance was not found.
75
+ */
76
+ getRegisteredInstanceType(componentConnectorType: string, features?: string[]): string;
77
+ /**
78
+ * Get the registered instance type for the component/connector if it exists.
79
+ * @param componentConnectorType The type of the component/connector.
80
+ * @param features The requested features of the component, if not specified the default entry will be retrieved.
81
+ * @returns The instance type matching the criteria if one is registered.
82
+ */
83
+ getRegisteredInstanceTypeOptional(componentConnectorType: string, features?: string[]): string | undefined;
70
84
  /**
71
85
  * Get the data required to create a clone of the engine.
72
86
  * @returns The clone data.
package/docs/changelog.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @twin.org/engine-core - Changelog
2
2
 
3
+ ## [0.0.2-next.6](https://github.com/twinfoundation/engine/compare/engine-core-v0.0.2-next.5...engine-core-v0.0.2-next.6) (2025-08-21)
4
+
5
+
6
+ ### Features
7
+
8
+ * update framework core ([acc0f8d](https://github.com/twinfoundation/engine/commit/acc0f8d455a4b8ec47f1da643139fa0f07775fa6))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/engine-models bumped from 0.0.2-next.5 to 0.0.2-next.6
16
+
17
+ ## [0.0.2-next.5](https://github.com/twinfoundation/engine/compare/engine-core-v0.0.2-next.4...engine-core-v0.0.2-next.5) (2025-08-14)
18
+
19
+
20
+ ### Features
21
+
22
+ * add synchronised storage support ([5142e34](https://github.com/twinfoundation/engine/commit/5142e3488f09195cf9f48a9c6c6d1014231a4c2c))
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * dependencies
29
+ * @twin.org/engine-models bumped from 0.0.2-next.4 to 0.0.2-next.5
30
+
3
31
  ## [0.0.2-next.4](https://github.com/twinfoundation/engine/compare/engine-core-v0.0.2-next.3...engine-core-v0.0.2-next.4) (2025-07-25)
4
32
 
5
33
 
@@ -46,14 +46,6 @@ Name for the engine logger.
46
46
 
47
47
  ***
48
48
 
49
- ### CLASS\_NAME
50
-
51
- > `readonly` **CLASS\_NAME**: `string`
52
-
53
- Runtime name for the class.
54
-
55
- ***
56
-
57
49
  ### \_context
58
50
 
59
51
  > `protected` **\_context**: `IEngineCoreContext`\<`C`, `S`\>
@@ -224,21 +216,89 @@ The state of the engine.
224
216
 
225
217
  ***
226
218
 
227
- ### getDefaultTypes()
219
+ ### getRegisteredInstances()
228
220
 
229
- > **getDefaultTypes**(): `object`
221
+ > **getRegisteredInstances**(): `object`
230
222
 
231
- Get the types for the component.
223
+ Get all the registered instances.
232
224
 
233
225
  #### Returns
234
226
 
235
227
  `object`
236
228
 
237
- The default types.
229
+ The registered instances.
230
+
231
+ #### Implementation of
232
+
233
+ `IEngineCore.getRegisteredInstances`
234
+
235
+ ***
236
+
237
+ ### getRegisteredInstanceType()
238
+
239
+ > **getRegisteredInstanceType**(`componentConnectorType`, `features?`): `string`
240
+
241
+ Get the registered instance type for the component/connector.
242
+
243
+ #### Parameters
244
+
245
+ ##### componentConnectorType
246
+
247
+ `string`
248
+
249
+ The type of the component/connector.
250
+
251
+ ##### features?
252
+
253
+ `string`[]
254
+
255
+ The requested features of the component, if not specified the default entry will be retrieved.
256
+
257
+ #### Returns
258
+
259
+ `string`
260
+
261
+ The instance type matching the criteria if one is registered.
262
+
263
+ #### Throws
264
+
265
+ If a matching instance was not found.
266
+
267
+ #### Implementation of
268
+
269
+ `IEngineCore.getRegisteredInstanceType`
270
+
271
+ ***
272
+
273
+ ### getRegisteredInstanceTypeOptional()
274
+
275
+ > **getRegisteredInstanceTypeOptional**(`componentConnectorType`, `features?`): `undefined` \| `string`
276
+
277
+ Get the registered instance type for the component/connector if it exists.
278
+
279
+ #### Parameters
280
+
281
+ ##### componentConnectorType
282
+
283
+ `string`
284
+
285
+ The type of the component/connector.
286
+
287
+ ##### features?
288
+
289
+ `string`[]
290
+
291
+ The requested features of the component, if not specified the default entry will be retrieved.
292
+
293
+ #### Returns
294
+
295
+ `undefined` \| `string`
296
+
297
+ The instance type matching the criteria if one is registered.
238
298
 
239
299
  #### Implementation of
240
300
 
241
- `IEngineCore.getDefaultTypes`
301
+ `IEngineCore.getRegisteredInstanceTypeOptional`
242
302
 
243
303
  ***
244
304
 
package/locales/en.json CHANGED
@@ -6,7 +6,8 @@
6
6
  "entityStorageCustomMissing": "Entity storage custom \"{typeCustom}\" missing for component \"{storageName}\"",
7
7
  "entityStorageMissing": "Entity storage configuration missing for component \"{storageName}\"",
8
8
  "bootstrapFailed": "Bootstrap failed for component \"{component}\", please check the logs for more information",
9
- "componentStopFailed": "Failed to stop component \"{component}\""
9
+ "componentStopFailed": "Failed to stop component \"{component}\"",
10
+ "instanceTypeNotFound": "Instance type not found for \"{type}\" with features \"{features}\""
10
11
  },
11
12
  "fileStateStorage": {
12
13
  "failedLoading": "Failed to load file state storage from \"{filename}\"",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/engine-core",
3
- "version": "0.0.2-next.4",
3
+ "version": "0.0.2-next.6",
4
4
  "description": "Engine core.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  "@twin.org/core": "next",
18
18
  "@twin.org/crypto": "next",
19
19
  "@twin.org/data-core": "next",
20
- "@twin.org/engine-models": "0.0.2-next.4",
20
+ "@twin.org/engine-models": "0.0.2-next.6",
21
21
  "@twin.org/entity": "next",
22
22
  "@twin.org/logging-connector-console": "next",
23
23
  "@twin.org/logging-models": "next",