@twin.org/engine-core 0.0.3-next.5 → 0.0.3-next.51

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/docs/examples.md CHANGED
@@ -1 +1,156 @@
1
- # @twin.org/engine-core - Examples
1
+ # Engine Core Examples
2
+
3
+ These examples focus on common runtime patterns such as lifecycle control, cloning, state storage, and module loading.
4
+
5
+ ## EngineCore
6
+
7
+ ```typescript
8
+ import { EngineCore, MemoryStateStorage } from '@twin.org/engine-core';
9
+ import type { IEngineCoreConfig, IEngineState } from '@twin.org/engine-models';
10
+
11
+ interface AppState extends IEngineState {
12
+ runCount?: number;
13
+ }
14
+
15
+ const config: IEngineCoreConfig = {
16
+ debug: true,
17
+ silent: false,
18
+ types: {}
19
+ };
20
+
21
+ const stateStorage = new MemoryStateStorage<AppState>();
22
+ const engineCore = new EngineCore<IEngineCoreConfig, AppState>({
23
+ config,
24
+ stateStorage,
25
+ skipBootstrap: true
26
+ });
27
+
28
+ engineCore.addTypeInitialiser('loggingConnector', '@twin.org/engine-types', 'initLoggingConnector');
29
+ console.log(engineCore.getTypeConfig('loggingConnector')?.length ?? 0); // 0
30
+
31
+ engineCore.addContextIdKey('tenant', ['tenant']);
32
+ engineCore.addContextId('tenant', 'tenant-a');
33
+
34
+ console.log(engineCore.getContextIdKeys()); // ["tenant"]
35
+ console.log(engineCore.getContextIds()); // { tenant: "tenant-a" }
36
+ console.log(engineCore.isPrimary()); // true
37
+ console.log(engineCore.isClone()); // false
38
+ console.log(engineCore.isStarted()); // false
39
+
40
+ await engineCore.start(true);
41
+ console.log(engineCore.isStarted()); // true
42
+
43
+ await engineCore.logInfo('Engine started for tenant-a');
44
+
45
+ const state = engineCore.getState();
46
+ state.runCount = (state.runCount ?? 0) + 1;
47
+ engineCore.setStateDirty();
48
+
49
+ console.log(engineCore.getConfig().debug); // true
50
+ console.log(engineCore.getRegisteredInstances()); // { loggingConnector: [...], loggingComponent: [...] }
51
+ console.log(engineCore.getRegisteredInstanceType('loggingComponent')); // "engine-logging-service"
52
+ console.log(engineCore.getRegisteredInstanceTypeOptional('hostingComponent')); // undefined
53
+
54
+ await engineCore.stop();
55
+ console.log(engineCore.isStarted()); // false
56
+ ```
57
+
58
+ ```typescript
59
+ import { BaseError, GeneralError } from '@twin.org/core';
60
+ import { EngineCore } from '@twin.org/engine-core';
61
+
62
+ const engineCore = new EngineCore({
63
+ config: { debug: false, silent: true, types: {} },
64
+ skipBootstrap: true
65
+ });
66
+
67
+ const wrapped = new GeneralError(
68
+ EngineCore.CLASS_NAME,
69
+ 'sampleFailure',
70
+ { area: 'examples' },
71
+ BaseError.fromError(new Error('Sample failure'))
72
+ );
73
+ await engineCore.logError(wrapped);
74
+ ```
75
+
76
+ ```typescript
77
+ import { EngineCore } from '@twin.org/engine-core';
78
+
79
+ const primary = new EngineCore({
80
+ config: { debug: false, silent: true, types: {} },
81
+ skipBootstrap: true
82
+ });
83
+ primary.addContextIdKey('node', ['node']);
84
+
85
+ const cloneData = primary.getCloneData();
86
+
87
+ const clone = new EngineCore({ config: { debug: false, silent: true, types: {} } });
88
+ clone.populateClone(cloneData, { node: 'node-1', tenant: 'tenant-a' }, true);
89
+
90
+ console.log(clone.isClone()); // true
91
+ console.log(clone.getContextIds()); // { node: "node-1", tenant: "tenant-a" }
92
+ ```
93
+
94
+ ## FileStateStorage
95
+
96
+ ```typescript
97
+ import { EngineCore, FileStateStorage } from '@twin.org/engine-core';
98
+
99
+ const engineCore = new EngineCore({
100
+ config: { debug: false, silent: true, types: {} },
101
+ skipBootstrap: true
102
+ });
103
+ const fileStorage = new FileStateStorage('./data/engine-state.json');
104
+
105
+ await fileStorage.save(engineCore, { ready: true });
106
+
107
+ const loaded = await fileStorage.load(engineCore);
108
+ console.log(loaded); // { ready: true }
109
+ ```
110
+
111
+ ## MemoryStateStorage
112
+
113
+ ```typescript
114
+ import { EngineCore, MemoryStateStorage } from '@twin.org/engine-core';
115
+
116
+ const engineCore = new EngineCore({
117
+ config: { debug: false, silent: true, types: {} },
118
+ skipBootstrap: true
119
+ });
120
+ const memoryStorage = new MemoryStateStorage(false, { retries: 1 });
121
+
122
+ console.log(await memoryStorage.load(engineCore)); // { retries: 1 }
123
+
124
+ await memoryStorage.save(engineCore, { retries: 2 });
125
+ console.log(await memoryStorage.load(engineCore)); // { retries: 2 }
126
+ ```
127
+
128
+ ## EngineModuleHelper
129
+
130
+ ```typescript
131
+ import { EngineCore, EngineModuleHelper } from '@twin.org/engine-core';
132
+ import type { IEngineModuleConfig } from '@twin.org/engine-models';
133
+
134
+ const engineCore = new EngineCore({
135
+ config: { debug: false, silent: true, types: {} },
136
+ skipBootstrap: true
137
+ });
138
+
139
+ const moduleConfig: IEngineModuleConfig = {
140
+ moduleName: '@twin.org/logging-service',
141
+ className: 'LoggingService',
142
+ dependencies: [
143
+ {
144
+ propertyName: 'loggingConnectorType',
145
+ componentName: 'loggingConnector',
146
+ isOptional: false
147
+ }
148
+ ],
149
+ config: {
150
+ level: 'info'
151
+ }
152
+ };
153
+
154
+ const component = await EngineModuleHelper.loadComponent<unknown>(engineCore, moduleConfig);
155
+ console.log(component !== undefined); // true
156
+ ```
@@ -38,7 +38,7 @@ The options for the engine.
38
38
 
39
39
  ## Properties
40
40
 
41
- ### LOGGING\_COMPONENT\_TYPE\_NAME
41
+ ### LOGGING\_COMPONENT\_TYPE\_NAME {#logging_component_type_name}
42
42
 
43
43
  > `readonly` `static` **LOGGING\_COMPONENT\_TYPE\_NAME**: `string` = `"engine-logging-service"`
44
44
 
@@ -46,7 +46,7 @@ Name for the engine logger component, used for direct console logging.
46
46
 
47
47
  ***
48
48
 
49
- ### LOGGING\_CONNECTOR\_TYPE\_NAME
49
+ ### LOGGING\_CONNECTOR\_TYPE\_NAME {#logging_connector_type_name}
50
50
 
51
51
  > `readonly` `static` **LOGGING\_CONNECTOR\_TYPE\_NAME**: `string` = `"engine-logging-connector"`
52
52
 
@@ -54,7 +54,7 @@ Name for the engine logger connector, used for direct console logging.
54
54
 
55
55
  ***
56
56
 
57
- ### CLASS\_NAME
57
+ ### CLASS\_NAME {#class_name}
58
58
 
59
59
  > `readonly` `static` **CLASS\_NAME**: `string`
60
60
 
@@ -62,7 +62,7 @@ Runtime name for the class.
62
62
 
63
63
  ***
64
64
 
65
- ### \_context
65
+ ### \_context {#_context}
66
66
 
67
67
  > `protected` **\_context**: `IEngineCoreContext`\<`C`, `S`\>
68
68
 
@@ -70,7 +70,7 @@ The core context.
70
70
 
71
71
  ***
72
72
 
73
- ### \_contextIdKeys
73
+ ### \_contextIdKeys {#_contextidkeys}
74
74
 
75
75
  > `protected` `readonly` **\_contextIdKeys**: `object`[]
76
76
 
@@ -86,15 +86,15 @@ The context ID keys.
86
86
 
87
87
  ***
88
88
 
89
- ### \_contextIds?
89
+ ### \_contextIds? {#_contextids}
90
90
 
91
- > `protected` `optional` **\_contextIds**: `IContextIds`
91
+ > `protected` `optional` **\_contextIds?**: `IContextIds`
92
92
 
93
93
  The context IDs.
94
94
 
95
95
  ## Methods
96
96
 
97
- ### addTypeInitialiser()
97
+ ### addTypeInitialiser() {#addtypeinitialiser}
98
98
 
99
99
  > **addTypeInitialiser**(`type`, `module`, `method`): `void`
100
100
 
@@ -130,9 +130,9 @@ The name of the method to call.
130
130
 
131
131
  ***
132
132
 
133
- ### getTypeConfig()
133
+ ### getTypeConfig() {#gettypeconfig}
134
134
 
135
- > **getTypeConfig**(`type`): `undefined` \| `IEngineCoreTypeConfig`[]
135
+ > **getTypeConfig**(`type`): `IEngineCoreTypeConfig`[] \| `undefined`
136
136
 
137
137
  Get the type config for a specific type.
138
138
 
@@ -146,7 +146,7 @@ The type to get the config for.
146
146
 
147
147
  #### Returns
148
148
 
149
- `undefined` \| `IEngineCoreTypeConfig`[]
149
+ `IEngineCoreTypeConfig`[] \| `undefined`
150
150
 
151
151
  The type config or undefined if not found.
152
152
 
@@ -156,7 +156,7 @@ The type config or undefined if not found.
156
156
 
157
157
  ***
158
158
 
159
- ### addContextIdKey()
159
+ ### addContextIdKey() {#addcontextidkey}
160
160
 
161
161
  > **addContextIdKey**(`key`, `componentFeatures`): `void`
162
162
 
@@ -186,7 +186,7 @@ The component features for the context ID handler.
186
186
 
187
187
  ***
188
188
 
189
- ### getContextIdKeys()
189
+ ### getContextIdKeys() {#getcontextidkeys}
190
190
 
191
191
  > **getContextIdKeys**(): `string`[]
192
192
 
@@ -204,7 +204,7 @@ The context IDs keys.
204
204
 
205
205
  ***
206
206
 
207
- ### addContextId()
207
+ ### addContextId() {#addcontextid}
208
208
 
209
209
  > **addContextId**(`key`, `value`): `void`
210
210
 
@@ -234,15 +234,15 @@ The context ID value.
234
234
 
235
235
  ***
236
236
 
237
- ### getContextIds()
237
+ ### getContextIds() {#getcontextids}
238
238
 
239
- > **getContextIds**(): `undefined` \| `IContextIds`
239
+ > **getContextIds**(): `IContextIds` \| `undefined`
240
240
 
241
241
  Get the context IDs for the engine.
242
242
 
243
243
  #### Returns
244
244
 
245
- `undefined` \| `IContextIds`
245
+ `IContextIds` \| `undefined`
246
246
 
247
247
  The context IDs or undefined if none are set.
248
248
 
@@ -252,17 +252,25 @@ The context IDs or undefined if none are set.
252
252
 
253
253
  ***
254
254
 
255
- ### start()
255
+ ### start() {#start}
256
256
 
257
- > **start**(): `Promise`\<`boolean`\>
257
+ > **start**(`skipComponentStart?`): `Promise`\<`void`\>
258
258
 
259
259
  Start the engine core.
260
260
 
261
+ #### Parameters
262
+
263
+ ##### skipComponentStart?
264
+
265
+ `boolean`
266
+
267
+ Should the component start be skipped.
268
+
261
269
  #### Returns
262
270
 
263
- `Promise`\<`boolean`\>
271
+ `Promise`\<`void`\>
264
272
 
265
- True if the start was successful.
273
+ A promise that resolves when the engine and all components have started.
266
274
 
267
275
  #### Implementation of
268
276
 
@@ -270,7 +278,7 @@ True if the start was successful.
270
278
 
271
279
  ***
272
280
 
273
- ### stop()
281
+ ### stop() {#stop}
274
282
 
275
283
  > **stop**(): `Promise`\<`void`\>
276
284
 
@@ -280,7 +288,7 @@ Stop the engine core.
280
288
 
281
289
  `Promise`\<`void`\>
282
290
 
283
- Nothing.
291
+ A promise that resolves when all components have stopped and state has been saved.
284
292
 
285
293
  #### Implementation of
286
294
 
@@ -288,7 +296,7 @@ Nothing.
288
296
 
289
297
  ***
290
298
 
291
- ### isStarted()
299
+ ### isStarted() {#isstarted}
292
300
 
293
301
  > **isStarted**(): `boolean`
294
302
 
@@ -306,7 +314,7 @@ True if the engine is started.
306
314
 
307
315
  ***
308
316
 
309
- ### isPrimary()
317
+ ### isPrimary() {#isprimary}
310
318
 
311
319
  > **isPrimary**(): `boolean`
312
320
 
@@ -324,7 +332,7 @@ True if the engine is the primary instance.
324
332
 
325
333
  ***
326
334
 
327
- ### isClone()
335
+ ### isClone() {#isclone}
328
336
 
329
337
  > **isClone**(): `boolean`
330
338
 
@@ -342,7 +350,7 @@ True if the engine instance is a clone.
342
350
 
343
351
  ***
344
352
 
345
- ### logInfo()
353
+ ### logInfo() {#loginfo}
346
354
 
347
355
  > **logInfo**(`message`): `Promise`\<`void`\>
348
356
 
@@ -360,13 +368,15 @@ The message to log.
360
368
 
361
369
  `Promise`\<`void`\>
362
370
 
371
+ A promise that resolves when the message has been logged.
372
+
363
373
  #### Implementation of
364
374
 
365
375
  `IEngineCore.logInfo`
366
376
 
367
377
  ***
368
378
 
369
- ### logError()
379
+ ### logError() {#logerror}
370
380
 
371
381
  > **logError**(`error`): `Promise`\<`void`\>
372
382
 
@@ -384,13 +394,15 @@ The error to log.
384
394
 
385
395
  `Promise`\<`void`\>
386
396
 
397
+ A promise that resolves when the error has been logged.
398
+
387
399
  #### Implementation of
388
400
 
389
401
  `IEngineCore.logError`
390
402
 
391
403
  ***
392
404
 
393
- ### getConfig()
405
+ ### getConfig() {#getconfig}
394
406
 
395
407
  > **getConfig**(): `C`
396
408
 
@@ -408,7 +420,7 @@ The config for the engine.
408
420
 
409
421
  ***
410
422
 
411
- ### getState()
423
+ ### getState() {#getstate}
412
424
 
413
425
  > **getState**(): `S`
414
426
 
@@ -426,7 +438,23 @@ The state of the engine.
426
438
 
427
439
  ***
428
440
 
429
- ### getRegisteredInstances()
441
+ ### setStateDirty() {#setstatedirty}
442
+
443
+ > **setStateDirty**(): `void`
444
+
445
+ Set the state to dirty so it gets saved.
446
+
447
+ #### Returns
448
+
449
+ `void`
450
+
451
+ #### Implementation of
452
+
453
+ `IEngineCore.setStateDirty`
454
+
455
+ ***
456
+
457
+ ### getRegisteredInstances() {#getregisteredinstances}
430
458
 
431
459
  > **getRegisteredInstances**(): `object`
432
460
 
@@ -444,7 +472,7 @@ The registered instances.
444
472
 
445
473
  ***
446
474
 
447
- ### getRegisteredInstanceType()
475
+ ### getRegisteredInstanceType() {#getregisteredinstancetype}
448
476
 
449
477
  > **getRegisteredInstanceType**(`componentConnectorType`, `features?`): `string`
450
478
 
@@ -480,9 +508,9 @@ If a matching instance was not found.
480
508
 
481
509
  ***
482
510
 
483
- ### getRegisteredInstanceTypeOptional()
511
+ ### getRegisteredInstanceTypeOptional() {#getregisteredinstancetypeoptional}
484
512
 
485
- > **getRegisteredInstanceTypeOptional**(`componentConnectorType`, `features?`): `undefined` \| `string`
513
+ > **getRegisteredInstanceTypeOptional**(`componentConnectorType`, `features?`): `string` \| `undefined`
486
514
 
487
515
  Get the registered instance type for the component/connector if it exists.
488
516
 
@@ -502,7 +530,7 @@ The requested features of the component, if not specified the default entry will
502
530
 
503
531
  #### Returns
504
532
 
505
- `undefined` \| `string`
533
+ `string` \| `undefined`
506
534
 
507
535
  The instance type matching the criteria if one is registered.
508
536
 
@@ -512,7 +540,83 @@ The instance type matching the criteria if one is registered.
512
540
 
513
541
  ***
514
542
 
515
- ### getCloneData()
543
+ ### getRegisteredLoggerType() {#getregisteredloggertype}
544
+
545
+ > **getRegisteredLoggerType**(`componentName`): `string` \| `undefined`
546
+
547
+ Get the registered logger for the component/connector.
548
+
549
+ #### Parameters
550
+
551
+ ##### componentName
552
+
553
+ `string`
554
+
555
+ The name of the component to get the logger for.
556
+
557
+ #### Returns
558
+
559
+ `string` \| `undefined`
560
+
561
+ The logger type name if one is registered and not silenced.
562
+
563
+ #### Implementation of
564
+
565
+ `IEngineCore.getRegisteredLoggerType`
566
+
567
+ ***
568
+
569
+ ### getRegisteredComponents() {#getregisteredcomponents}
570
+
571
+ > **getRegisteredComponents**(): `Promise`\<`object`[]\>
572
+
573
+ Get the registered components.
574
+
575
+ #### Returns
576
+
577
+ `Promise`\<`object`[]\>
578
+
579
+ The registered components.
580
+
581
+ #### Implementation of
582
+
583
+ `IEngineCore.getRegisteredComponents`
584
+
585
+ ***
586
+
587
+ ### addRegisteredComponent() {#addregisteredcomponent}
588
+
589
+ > **addRegisteredComponent**(`instanceType`, `component`): `Promise`\<`void`\>
590
+
591
+ Add a registered component to the engine.
592
+
593
+ #### Parameters
594
+
595
+ ##### instanceType
596
+
597
+ `string`
598
+
599
+ The instance type to register the component under.
600
+
601
+ ##### component
602
+
603
+ `IComponent`
604
+
605
+ The component to register.
606
+
607
+ #### Returns
608
+
609
+ `Promise`\<`void`\>
610
+
611
+ A promise that resolves when the component has been registered.
612
+
613
+ #### Implementation of
614
+
615
+ `IEngineCore.addRegisteredComponent`
616
+
617
+ ***
618
+
619
+ ### getCloneData() {#getclonedata}
516
620
 
517
621
  > **getCloneData**(): `IEngineCoreClone`\<`C`, `S`\>
518
622
 
@@ -530,9 +634,9 @@ The clone data.
530
634
 
531
635
  ***
532
636
 
533
- ### populateClone()
637
+ ### populateClone() {#populateclone}
534
638
 
535
- > **populateClone**(`cloneData`, `silent?`): `void`
639
+ > **populateClone**(`cloneData`, `contextIds?`, `silent?`): `void`
536
640
 
537
641
  Populate the engine from the clone data.
538
642
 
@@ -544,6 +648,12 @@ Populate the engine from the clone data.
544
648
 
545
649
  The clone data to populate from.
546
650
 
651
+ ##### contextIds?
652
+
653
+ `IContextIds`
654
+
655
+ The context IDs to use for the clone.
656
+
547
657
  ##### silent?
548
658
 
549
659
  `boolean`
@@ -16,7 +16,7 @@ Store state in a file.
16
16
 
17
17
  ### Constructor
18
18
 
19
- > **new FileStateStorage**\<`S`\>(`filename`, `readonlyMode`): `FileStateStorage`\<`S`\>
19
+ > **new FileStateStorage**\<`S`\>(`filename`, `readonlyMode?`): `FileStateStorage`\<`S`\>
20
20
 
21
21
  Create a new instance of FileStateStorage.
22
22
 
@@ -28,7 +28,7 @@ Create a new instance of FileStateStorage.
28
28
 
29
29
  The filename to store the state.
30
30
 
31
- ##### readonlyMode
31
+ ##### readonlyMode?
32
32
 
33
33
  `boolean` = `false`
34
34
 
@@ -40,7 +40,7 @@ Whether the file is in read-only mode.
40
40
 
41
41
  ## Properties
42
42
 
43
- ### CLASS\_NAME
43
+ ### CLASS\_NAME {#class_name}
44
44
 
45
45
  > `readonly` `static` **CLASS\_NAME**: `string`
46
46
 
@@ -48,9 +48,9 @@ Runtime name for the class.
48
48
 
49
49
  ## Methods
50
50
 
51
- ### load()
51
+ ### load() {#load}
52
52
 
53
- > **load**(`engineCore`): `Promise`\<`undefined` \| `S`\>
53
+ > **load**(`engineCore`): `Promise`\<`S` \| `undefined`\>
54
54
 
55
55
  Method for loading the state.
56
56
 
@@ -64,7 +64,7 @@ The engine core to load the state for.
64
64
 
65
65
  #### Returns
66
66
 
67
- `Promise`\<`undefined` \| `S`\>
67
+ `Promise`\<`S` \| `undefined`\>
68
68
 
69
69
  The state of the engine or undefined if it doesn't exist.
70
70
 
@@ -74,7 +74,7 @@ The state of the engine or undefined if it doesn't exist.
74
74
 
75
75
  ***
76
76
 
77
- ### save()
77
+ ### save() {#save}
78
78
 
79
79
  > **save**(`engineCore`, `state`): `Promise`\<`void`\>
80
80
 
@@ -98,7 +98,7 @@ The state of the engine to save.
98
98
 
99
99
  `Promise`\<`void`\>
100
100
 
101
- Nothing.
101
+ A promise that resolves when the state has been written to disk.
102
102
 
103
103
  #### Implementation of
104
104
 
@@ -16,13 +16,13 @@ Store state in memory.
16
16
 
17
17
  ### Constructor
18
18
 
19
- > **new MemoryStateStorage**\<`S`\>(`readonlyMode`, `state?`): `MemoryStateStorage`\<`S`\>
19
+ > **new MemoryStateStorage**\<`S`\>(`readonlyMode?`, `state?`): `MemoryStateStorage`\<`S`\>
20
20
 
21
21
  Create a new instance of MemoryStateStorage.
22
22
 
23
23
  #### Parameters
24
24
 
25
- ##### readonlyMode
25
+ ##### readonlyMode?
26
26
 
27
27
  `boolean` = `false`
28
28
 
@@ -40,7 +40,7 @@ The initial state.
40
40
 
41
41
  ## Properties
42
42
 
43
- ### CLASS\_NAME
43
+ ### CLASS\_NAME {#class_name}
44
44
 
45
45
  > `readonly` `static` **CLASS\_NAME**: `string`
46
46
 
@@ -48,9 +48,9 @@ Runtime name for the class.
48
48
 
49
49
  ## Methods
50
50
 
51
- ### load()
51
+ ### load() {#load}
52
52
 
53
- > **load**(`engineCore`): `Promise`\<`undefined` \| `S`\>
53
+ > **load**(`engineCore`): `Promise`\<`S` \| `undefined`\>
54
54
 
55
55
  Method for loading the state.
56
56
 
@@ -64,7 +64,7 @@ The engine core to load the state for.
64
64
 
65
65
  #### Returns
66
66
 
67
- `Promise`\<`undefined` \| `S`\>
67
+ `Promise`\<`S` \| `undefined`\>
68
68
 
69
69
  The state of the engine or undefined if it doesn't exist.
70
70
 
@@ -74,7 +74,7 @@ The state of the engine or undefined if it doesn't exist.
74
74
 
75
75
  ***
76
76
 
77
- ### save()
77
+ ### save() {#save}
78
78
 
79
79
  > **save**(`engineCore`, `state`): `Promise`\<`void`\>
80
80
 
@@ -98,7 +98,7 @@ The state of the engine to save.
98
98
 
99
99
  `Promise`\<`void`\>
100
100
 
101
- Nothing.
101
+ A promise that resolves when the state has been stored in memory.
102
102
 
103
103
  #### Implementation of
104
104
 
@@ -5,7 +5,6 @@
5
5
  - [EngineCore](classes/EngineCore.md)
6
6
  - [FileStateStorage](classes/FileStateStorage.md)
7
7
  - [MemoryStateStorage](classes/MemoryStateStorage.md)
8
- - [EngineModuleHelper](classes/EngineModuleHelper.md)
9
8
 
10
9
  ## Interfaces
11
10