@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/engine-core",
3
- "version": "0.0.2-next.9",
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.2-next.9",
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/cjs/index.cjs",
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
- "require": "./dist/cjs/index.cjs",
35
- "import": "./dist/esm/index.mjs"
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/cjs",
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
  }
@@ -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;