@twin.org/engine-core 0.0.1-next.28 → 0.0.1-next.29

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.
@@ -139,7 +139,7 @@ class EngineCore {
139
139
  config: options.config,
140
140
  defaultTypes: {},
141
141
  componentInstances: [],
142
- state: { bootstrappedComponents: [] },
142
+ state: { bootstrappedComponents: [], componentStates: {} },
143
143
  stateDirty: false
144
144
  };
145
145
  this._stateStorage = options.stateStorage;
@@ -178,27 +178,44 @@ class EngineCore {
178
178
  if (this._context.config.debug) {
179
179
  this.logInfo(core.I18n.formatMessage("engineCore.debuggingEnabled"));
180
180
  }
181
- let canContinue = await this.stateLoad();
182
- if (canContinue) {
183
- for (const { type, typeConfig, module, method } of this._typeInitialisers) {
184
- await this.initialiseTypeConfig(type, typeConfig, module, method);
185
- }
186
- canContinue = await this.bootstrap();
181
+ let canContinue;
182
+ try {
183
+ canContinue = await this.stateLoad();
187
184
  if (canContinue) {
185
+ for (const { type, typeConfig, module, method } of this._typeInitialisers) {
186
+ await this.initialiseTypeConfig(type, typeConfig, module, method);
187
+ }
188
+ await this.bootstrap();
188
189
  this.logInfo(core.I18n.formatMessage("engineCore.componentsStarting"));
189
190
  for (const instance of this._context.componentInstances) {
190
191
  if (core.Is.function(instance.component.start)) {
191
- this.logInfo(core.I18n.formatMessage("engineCore.componentStarting", { element: instance.instanceType }));
192
- await instance.component.start(this._context.state.nodeIdentity, this._loggerTypeName);
192
+ const instanceName = this.getInstanceName(instance);
193
+ this.logInfo(core.I18n.formatMessage("engineCore.componentStarting", {
194
+ element: instance.instanceType
195
+ }));
196
+ const componentState = this._context.state.componentStates[instanceName] ?? {};
197
+ const lastState = core.ObjectHelper.clone(componentState);
198
+ await instance.component.start(this._context.state.nodeIdentity, this._loggerTypeName, componentState);
199
+ if (!core.ObjectHelper.equal(lastState, componentState)) {
200
+ this._context.state.componentStates[instanceName] = componentState;
201
+ this._context.stateDirty = true;
202
+ }
193
203
  }
194
204
  }
195
205
  this.logInfo(core.I18n.formatMessage("engineCore.componentsComplete"));
196
206
  }
197
- }
198
- if (canContinue) {
199
207
  this.logInfo(core.I18n.formatMessage("engineCore.started"));
200
208
  this._isStarted = true;
201
209
  }
210
+ catch (err) {
211
+ canContinue = false;
212
+ this.logError(core.BaseError.fromError(err));
213
+ }
214
+ finally {
215
+ if (!(await this.stateSave())) {
216
+ canContinue = false;
217
+ }
218
+ }
202
219
  return canContinue;
203
220
  }
204
221
  /**
@@ -210,9 +227,16 @@ class EngineCore {
210
227
  this.logInfo(core.I18n.formatMessage("engineCore.componentsStopping"));
211
228
  for (const instance of this._context.componentInstances) {
212
229
  if (core.Is.function(instance.component.stop)) {
230
+ const instanceName = this.getInstanceName(instance);
231
+ const componentState = this._context.state.componentStates[instanceName] ?? {};
232
+ const lastState = core.ObjectHelper.clone(componentState);
213
233
  this.logInfo(core.I18n.formatMessage("engineCore.componentStopping", { element: instance.instanceType }));
214
234
  try {
215
- await instance.component.stop(this._context.state.nodeIdentity, this._loggerTypeName);
235
+ await instance.component.stop(this._context.state.nodeIdentity, this._loggerTypeName, componentState);
236
+ if (!core.ObjectHelper.equal(lastState, componentState)) {
237
+ this._context.state.componentStates[instanceName] = componentState;
238
+ this._context.stateDirty = true;
239
+ }
216
240
  }
217
241
  catch (err) {
218
242
  this.logError(new core.GeneralError(this.CLASS_NAME, "componentStopFailed", {
@@ -221,6 +245,7 @@ class EngineCore {
221
245
  }
222
246
  }
223
247
  }
248
+ await this.stateSave();
224
249
  this.logInfo(core.I18n.formatMessage("engineCore.componentsStopped"));
225
250
  this.logInfo(core.I18n.formatMessage("engineCore.stopped"));
226
251
  }
@@ -314,7 +339,7 @@ class EngineCore {
314
339
  config: cloneData.config,
315
340
  defaultTypes: {},
316
341
  componentInstances: [],
317
- state: { bootstrappedComponents: [] },
342
+ state: { bootstrappedComponents: [], componentStates: {} },
318
343
  stateDirty: false
319
344
  };
320
345
  this._typeInitialisers = cloneData.typeInitialisers;
@@ -373,9 +398,11 @@ class EngineCore {
373
398
  if (this._stateStorage) {
374
399
  try {
375
400
  this._context.state = ((await this._stateStorage.load(this)) ?? {
376
- bootstrappedComponents: []
401
+ bootstrappedComponents: [],
402
+ componentStates: {}
377
403
  });
378
404
  this._context.state.bootstrappedComponents ??= [];
405
+ this._context.state.componentStates ??= {};
379
406
  this._context.stateDirty = false;
380
407
  return true;
381
408
  }
@@ -407,54 +434,54 @@ class EngineCore {
407
434
  }
408
435
  /**
409
436
  * Bootstrap the engine.
410
- * @returns True if the engine can continue.
411
437
  * @internal
412
438
  */
413
439
  async bootstrap() {
414
- let canContinue = true;
415
440
  if (!this._skipBootstrap) {
416
441
  this.logInfo(core.I18n.formatMessage("engineCore.bootstrapStarted"));
417
- try {
418
- // First bootstrap the components.
419
- for (const instance of this._context.componentInstances) {
420
- if (core.Is.function(instance.component.bootstrap)) {
421
- const bootstrapName = `${instance.component.CLASS_NAME}-${instance.instanceType}`;
422
- if (!this._context.state.bootstrappedComponents.includes(bootstrapName)) {
423
- this.logInfo(core.I18n.formatMessage("engineCore.bootstrapping", {
424
- element: bootstrapName
425
- }));
426
- const bootstrapSuccess = await instance.component.bootstrap(this._loggerTypeName);
427
- // If the bootstrap method failed then throw an error
428
- if (!bootstrapSuccess) {
429
- throw new core.GeneralError(this.CLASS_NAME, "bootstrapFailed", {
430
- component: `${instance.component.CLASS_NAME}:${instance.instanceType}`
431
- });
432
- }
433
- // Otherwise add the component to the bootstrapped list and set the state as dirty
434
- this._context.state.bootstrappedComponents.push(bootstrapName);
435
- this._context.stateDirty = true;
442
+ // First bootstrap the components.
443
+ for (const instance of this._context.componentInstances) {
444
+ if (core.Is.function(instance.component.bootstrap)) {
445
+ const instanceName = this.getInstanceName(instance);
446
+ if (!this._context.state.bootstrappedComponents.includes(instanceName)) {
447
+ this.logInfo(core.I18n.formatMessage("engineCore.bootstrapping", {
448
+ element: instanceName
449
+ }));
450
+ const componentState = this._context.state.componentStates[instanceName] ?? {};
451
+ const lastState = core.ObjectHelper.clone(componentState);
452
+ const bootstrapSuccess = await instance.component.bootstrap(this._loggerTypeName, componentState);
453
+ // If the bootstrap method failed then throw an error
454
+ if (!bootstrapSuccess) {
455
+ throw new core.GeneralError(this.CLASS_NAME, "bootstrapFailed", {
456
+ component: `${instance.component.CLASS_NAME}:${instance.instanceType}`
457
+ });
458
+ }
459
+ // Otherwise add the component to the bootstrapped list and set the state as dirty
460
+ this._context.state.bootstrappedComponents.push(instanceName);
461
+ if (!core.ObjectHelper.equal(lastState, componentState)) {
462
+ this._context.state.componentStates[instanceName] = componentState;
436
463
  }
464
+ this._context.stateDirty = true;
437
465
  }
438
466
  }
439
- // Now perform any custom bootstrap operations
440
- if (canContinue && core.Is.function(this._customBootstrap)) {
441
- await this._customBootstrap(this, this._context);
442
- }
443
- }
444
- catch (err) {
445
- canContinue = false;
446
- this.logError(core.BaseError.fromError(err));
447
467
  }
448
- finally {
449
- if (await this.stateSave()) {
450
- this.logInfo(core.I18n.formatMessage("engineCore.bootstrapComplete"));
451
- }
452
- else {
453
- canContinue = false;
454
- }
468
+ // Now perform any custom bootstrap operations
469
+ if (core.Is.function(this._customBootstrap)) {
470
+ await this._customBootstrap(this, this._context);
455
471
  }
472
+ this.logInfo(core.I18n.formatMessage("engineCore.bootstrapComplete"));
456
473
  }
457
- return canContinue;
474
+ }
475
+ /**
476
+ * Get the instance name.
477
+ * @param instance The instance to get the name for.
478
+ * @param instance.instanceType The instance type.
479
+ * @param instance.component The component.
480
+ * @returns The instance name.
481
+ * @internal
482
+ */
483
+ getInstanceName(instance) {
484
+ return `${instance.component.CLASS_NAME}-${instance.instanceType}`;
458
485
  }
459
486
  }
460
487
 
@@ -1,4 +1,4 @@
1
- import { I18n, StringHelper, Is, GeneralError, BaseError, ErrorHelper, Guards } from '@twin.org/core';
1
+ import { I18n, StringHelper, Is, ObjectHelper, BaseError, GeneralError, ErrorHelper, Guards } from '@twin.org/core';
2
2
  import { EntitySchemaFactory } from '@twin.org/entity';
3
3
  import { ConsoleLoggingConnector } from '@twin.org/logging-connector-console';
4
4
  import { SilentLoggingConnector, LoggingConnectorFactory } from '@twin.org/logging-models';
@@ -137,7 +137,7 @@ class EngineCore {
137
137
  config: options.config,
138
138
  defaultTypes: {},
139
139
  componentInstances: [],
140
- state: { bootstrappedComponents: [] },
140
+ state: { bootstrappedComponents: [], componentStates: {} },
141
141
  stateDirty: false
142
142
  };
143
143
  this._stateStorage = options.stateStorage;
@@ -176,27 +176,44 @@ class EngineCore {
176
176
  if (this._context.config.debug) {
177
177
  this.logInfo(I18n.formatMessage("engineCore.debuggingEnabled"));
178
178
  }
179
- let canContinue = await this.stateLoad();
180
- if (canContinue) {
181
- for (const { type, typeConfig, module, method } of this._typeInitialisers) {
182
- await this.initialiseTypeConfig(type, typeConfig, module, method);
183
- }
184
- canContinue = await this.bootstrap();
179
+ let canContinue;
180
+ try {
181
+ canContinue = await this.stateLoad();
185
182
  if (canContinue) {
183
+ for (const { type, typeConfig, module, method } of this._typeInitialisers) {
184
+ await this.initialiseTypeConfig(type, typeConfig, module, method);
185
+ }
186
+ await this.bootstrap();
186
187
  this.logInfo(I18n.formatMessage("engineCore.componentsStarting"));
187
188
  for (const instance of this._context.componentInstances) {
188
189
  if (Is.function(instance.component.start)) {
189
- this.logInfo(I18n.formatMessage("engineCore.componentStarting", { element: instance.instanceType }));
190
- await instance.component.start(this._context.state.nodeIdentity, this._loggerTypeName);
190
+ const instanceName = this.getInstanceName(instance);
191
+ this.logInfo(I18n.formatMessage("engineCore.componentStarting", {
192
+ element: instance.instanceType
193
+ }));
194
+ const componentState = this._context.state.componentStates[instanceName] ?? {};
195
+ const lastState = ObjectHelper.clone(componentState);
196
+ await instance.component.start(this._context.state.nodeIdentity, this._loggerTypeName, componentState);
197
+ if (!ObjectHelper.equal(lastState, componentState)) {
198
+ this._context.state.componentStates[instanceName] = componentState;
199
+ this._context.stateDirty = true;
200
+ }
191
201
  }
192
202
  }
193
203
  this.logInfo(I18n.formatMessage("engineCore.componentsComplete"));
194
204
  }
195
- }
196
- if (canContinue) {
197
205
  this.logInfo(I18n.formatMessage("engineCore.started"));
198
206
  this._isStarted = true;
199
207
  }
208
+ catch (err) {
209
+ canContinue = false;
210
+ this.logError(BaseError.fromError(err));
211
+ }
212
+ finally {
213
+ if (!(await this.stateSave())) {
214
+ canContinue = false;
215
+ }
216
+ }
200
217
  return canContinue;
201
218
  }
202
219
  /**
@@ -208,9 +225,16 @@ class EngineCore {
208
225
  this.logInfo(I18n.formatMessage("engineCore.componentsStopping"));
209
226
  for (const instance of this._context.componentInstances) {
210
227
  if (Is.function(instance.component.stop)) {
228
+ const instanceName = this.getInstanceName(instance);
229
+ const componentState = this._context.state.componentStates[instanceName] ?? {};
230
+ const lastState = ObjectHelper.clone(componentState);
211
231
  this.logInfo(I18n.formatMessage("engineCore.componentStopping", { element: instance.instanceType }));
212
232
  try {
213
- await instance.component.stop(this._context.state.nodeIdentity, this._loggerTypeName);
233
+ await instance.component.stop(this._context.state.nodeIdentity, this._loggerTypeName, componentState);
234
+ if (!ObjectHelper.equal(lastState, componentState)) {
235
+ this._context.state.componentStates[instanceName] = componentState;
236
+ this._context.stateDirty = true;
237
+ }
214
238
  }
215
239
  catch (err) {
216
240
  this.logError(new GeneralError(this.CLASS_NAME, "componentStopFailed", {
@@ -219,6 +243,7 @@ class EngineCore {
219
243
  }
220
244
  }
221
245
  }
246
+ await this.stateSave();
222
247
  this.logInfo(I18n.formatMessage("engineCore.componentsStopped"));
223
248
  this.logInfo(I18n.formatMessage("engineCore.stopped"));
224
249
  }
@@ -312,7 +337,7 @@ class EngineCore {
312
337
  config: cloneData.config,
313
338
  defaultTypes: {},
314
339
  componentInstances: [],
315
- state: { bootstrappedComponents: [] },
340
+ state: { bootstrappedComponents: [], componentStates: {} },
316
341
  stateDirty: false
317
342
  };
318
343
  this._typeInitialisers = cloneData.typeInitialisers;
@@ -371,9 +396,11 @@ class EngineCore {
371
396
  if (this._stateStorage) {
372
397
  try {
373
398
  this._context.state = ((await this._stateStorage.load(this)) ?? {
374
- bootstrappedComponents: []
399
+ bootstrappedComponents: [],
400
+ componentStates: {}
375
401
  });
376
402
  this._context.state.bootstrappedComponents ??= [];
403
+ this._context.state.componentStates ??= {};
377
404
  this._context.stateDirty = false;
378
405
  return true;
379
406
  }
@@ -405,54 +432,54 @@ class EngineCore {
405
432
  }
406
433
  /**
407
434
  * Bootstrap the engine.
408
- * @returns True if the engine can continue.
409
435
  * @internal
410
436
  */
411
437
  async bootstrap() {
412
- let canContinue = true;
413
438
  if (!this._skipBootstrap) {
414
439
  this.logInfo(I18n.formatMessage("engineCore.bootstrapStarted"));
415
- try {
416
- // First bootstrap the components.
417
- for (const instance of this._context.componentInstances) {
418
- if (Is.function(instance.component.bootstrap)) {
419
- const bootstrapName = `${instance.component.CLASS_NAME}-${instance.instanceType}`;
420
- if (!this._context.state.bootstrappedComponents.includes(bootstrapName)) {
421
- this.logInfo(I18n.formatMessage("engineCore.bootstrapping", {
422
- element: bootstrapName
423
- }));
424
- const bootstrapSuccess = await instance.component.bootstrap(this._loggerTypeName);
425
- // If the bootstrap method failed then throw an error
426
- if (!bootstrapSuccess) {
427
- throw new GeneralError(this.CLASS_NAME, "bootstrapFailed", {
428
- component: `${instance.component.CLASS_NAME}:${instance.instanceType}`
429
- });
430
- }
431
- // Otherwise add the component to the bootstrapped list and set the state as dirty
432
- this._context.state.bootstrappedComponents.push(bootstrapName);
433
- this._context.stateDirty = true;
440
+ // First bootstrap the components.
441
+ for (const instance of this._context.componentInstances) {
442
+ if (Is.function(instance.component.bootstrap)) {
443
+ const instanceName = this.getInstanceName(instance);
444
+ if (!this._context.state.bootstrappedComponents.includes(instanceName)) {
445
+ this.logInfo(I18n.formatMessage("engineCore.bootstrapping", {
446
+ element: instanceName
447
+ }));
448
+ const componentState = this._context.state.componentStates[instanceName] ?? {};
449
+ const lastState = ObjectHelper.clone(componentState);
450
+ const bootstrapSuccess = await instance.component.bootstrap(this._loggerTypeName, componentState);
451
+ // If the bootstrap method failed then throw an error
452
+ if (!bootstrapSuccess) {
453
+ throw new GeneralError(this.CLASS_NAME, "bootstrapFailed", {
454
+ component: `${instance.component.CLASS_NAME}:${instance.instanceType}`
455
+ });
456
+ }
457
+ // Otherwise add the component to the bootstrapped list and set the state as dirty
458
+ this._context.state.bootstrappedComponents.push(instanceName);
459
+ if (!ObjectHelper.equal(lastState, componentState)) {
460
+ this._context.state.componentStates[instanceName] = componentState;
434
461
  }
462
+ this._context.stateDirty = true;
435
463
  }
436
464
  }
437
- // Now perform any custom bootstrap operations
438
- if (canContinue && Is.function(this._customBootstrap)) {
439
- await this._customBootstrap(this, this._context);
440
- }
441
- }
442
- catch (err) {
443
- canContinue = false;
444
- this.logError(BaseError.fromError(err));
445
465
  }
446
- finally {
447
- if (await this.stateSave()) {
448
- this.logInfo(I18n.formatMessage("engineCore.bootstrapComplete"));
449
- }
450
- else {
451
- canContinue = false;
452
- }
466
+ // Now perform any custom bootstrap operations
467
+ if (Is.function(this._customBootstrap)) {
468
+ await this._customBootstrap(this, this._context);
453
469
  }
470
+ this.logInfo(I18n.formatMessage("engineCore.bootstrapComplete"));
454
471
  }
455
- return canContinue;
472
+ }
473
+ /**
474
+ * Get the instance name.
475
+ * @param instance The instance to get the name for.
476
+ * @param instance.instanceType The instance type.
477
+ * @param instance.component The component.
478
+ * @returns The instance name.
479
+ * @internal
480
+ */
481
+ getInstanceName(instance) {
482
+ return `${instance.component.CLASS_NAME}-${instance.instanceType}`;
456
483
  }
457
484
  }
458
485
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/engine-core",
3
- "version": "0.0.1-next.28",
3
+ "version": "0.0.1-next.29",
4
4
  "description": "Engine core.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,7 +18,7 @@
18
18
  "@twin.org/crypto": "next",
19
19
  "@twin.org/data-core": "next",
20
20
  "@twin.org/data-schema-org": "next",
21
- "@twin.org/engine-models": "0.0.1-next.28",
21
+ "@twin.org/engine-models": "0.0.1-next.29",
22
22
  "@twin.org/entity": "next",
23
23
  "@twin.org/logging-connector-console": "next",
24
24
  "@twin.org/logging-models": "next",