@twin.org/node-core 0.0.2-next.16 → 0.0.2-next.18

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.
Files changed (38) hide show
  1. package/dist/cjs/index.cjs +211 -75
  2. package/dist/esm/index.mjs +203 -77
  3. package/dist/types/builders/engineServerEnvBuilder.d.ts +1 -1
  4. package/dist/types/builders/extensionsBuilder.d.ts +32 -0
  5. package/dist/types/defaults.d.ts +6 -0
  6. package/dist/types/index.d.ts +4 -0
  7. package/dist/types/models/IEngineEnvironmentVariables.d.ts +35 -14
  8. package/dist/types/models/INodeEngineConfig.d.ts +6 -0
  9. package/dist/types/models/INodeOptions.d.ts +2 -1
  10. package/dist/types/models/nodeExtensionMethods.d.ts +27 -0
  11. package/dist/types/node.d.ts +2 -2
  12. package/dist/types/server.d.ts +4 -2
  13. package/docs/changelog.md +15 -0
  14. package/docs/reference/functions/buildConfiguration.md +2 -2
  15. package/docs/reference/functions/buildEngineServerConfiguration.md +1 -1
  16. package/docs/reference/functions/extensionsConfiguration.md +25 -0
  17. package/docs/reference/functions/extensionsInitialiseEngine.md +25 -0
  18. package/docs/reference/functions/extensionsInitialiseEngineServer.md +31 -0
  19. package/docs/reference/functions/shutdownExtensions.md +25 -0
  20. package/docs/reference/functions/start.md +4 -4
  21. package/docs/reference/index.md +15 -0
  22. package/docs/reference/interfaces/IEngineEnvironmentVariables.md +73 -22
  23. package/docs/reference/interfaces/IEngineServerEnvironmentVariables.md +101 -30
  24. package/docs/reference/interfaces/INodeEngineConfig.md +7 -0
  25. package/docs/reference/interfaces/INodeEnvironmentVariables.md +101 -30
  26. package/docs/reference/interfaces/INodeOptions.md +6 -2
  27. package/docs/reference/type-aliases/NodeExtensionInitialiseEngineMethod.md +18 -0
  28. package/docs/reference/type-aliases/NodeExtensionInitialiseEngineServerMethod.md +24 -0
  29. package/docs/reference/type-aliases/NodeExtensionInitialiseMethod.md +23 -0
  30. package/docs/reference/type-aliases/NodeExtensionShutdownMethod.md +10 -0
  31. package/docs/reference/variables/ATTESTATION_VERIFICATION_METHOD_ID.md +3 -0
  32. package/docs/reference/variables/AUTH_SIGNING_KEY_ID.md +3 -0
  33. package/docs/reference/variables/BLOB_STORAGE_ENCRYPTION_KEY_ID.md +3 -0
  34. package/docs/reference/variables/IMMUTABLE_PROOF_VERIFICATION_METHOD_ID.md +3 -0
  35. package/docs/reference/variables/SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID.md +3 -0
  36. package/docs/reference/variables/VC_AUTHENTICATION_VERIFICATION_METHOD_ID.md +3 -0
  37. package/locales/en.json +9 -2
  38. package/package.json +13 -1
@@ -11,6 +11,7 @@ var vaultModels = require('@twin.org/vault-models');
11
11
  var walletModels = require('@twin.org/wallet-models');
12
12
  var promises = require('node:fs/promises');
13
13
  var path = require('node:path');
14
+ var cliCore = require('@twin.org/cli-core');
14
15
  var rightsManagementRestClient = require('@twin.org/rights-management-rest-client');
15
16
  var engineServer = require('@twin.org/engine-server');
16
17
  var modules = require('@twin.org/modules');
@@ -38,6 +39,15 @@ function _interopNamespaceDefault(e) {
38
39
 
39
40
  var dotenv__namespace = /*#__PURE__*/_interopNamespaceDefault(dotenv);
40
41
 
42
+ // Copyright 2024 IOTA Stiftung.
43
+ // SPDX-License-Identifier: Apache-2.0.
44
+ const ATTESTATION_VERIFICATION_METHOD_ID = "attestation-assertion";
45
+ const IMMUTABLE_PROOF_VERIFICATION_METHOD_ID = "immutable-proof-assertion";
46
+ const BLOB_STORAGE_ENCRYPTION_KEY_ID = "blob-encryption";
47
+ const SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID = "synchronised-storage-blob-encryption";
48
+ const VC_AUTHENTICATION_VERIFICATION_METHOD_ID = "node-authentication-assertion";
49
+ const AUTH_SIGNING_KEY_ID = "auth-signing";
50
+
41
51
  // Copyright 2024 IOTA Stiftung.
42
52
  // SPDX-License-Identifier: Apache-2.0.
43
53
  /**
@@ -61,20 +71,19 @@ const NodeFeatures = {
61
71
 
62
72
  // Copyright 2024 IOTA Stiftung.
63
73
  // SPDX-License-Identifier: Apache-2.0.
64
- /* eslint-disable no-console */
65
74
  /**
66
75
  * Initialise the locales for the application.
67
76
  * @param localesDirectory The directory containing the locales.
68
77
  */
69
78
  async function initialiseLocales(localesDirectory) {
70
79
  const localesFile = path.resolve(path.join(localesDirectory, "en.json"));
71
- console.info("Locales File:", localesFile);
80
+ cliCore.CLIDisplay.value("Locales File", localesFile);
72
81
  if (await fileExists(localesFile)) {
73
82
  const enLangContent = await promises.readFile(localesFile, "utf8");
74
83
  core.I18n.addDictionary("en", JSON.parse(enLangContent));
75
84
  }
76
85
  else {
77
- console.warn(`Locales file not found: ${localesFile}`);
86
+ cliCore.CLIDisplay.error(`Locales file not found: ${localesFile}`);
78
87
  }
79
88
  }
80
89
  /**
@@ -209,10 +218,16 @@ async function bootstrap(engineCore, context, envVars) {
209
218
  await bootstrapNodeUser(engineCore, context, envVars, features);
210
219
  await bootstrapAuth(engineCore, context, envVars);
211
220
  await bootstrapBlobEncryption(engineCore, context, envVars);
212
- await addVerificationMethod(engineCore, context, "attestation", envVars.attestationVerificationMethodId);
213
- await addVerificationMethod(engineCore, context, "immutable proof", envVars.immutableProofVerificationMethodId);
221
+ const defaultAttestationConnectorType = engineCore.getRegisteredInstanceTypeOptional("attestationConnector");
222
+ if (!core.Is.empty(defaultAttestationConnectorType)) {
223
+ await addVerificationMethod(engineCore, context, "attestation", envVars.attestationVerificationMethodId ?? ATTESTATION_VERIFICATION_METHOD_ID);
224
+ }
225
+ const defaultImmutableProofComponentType = engineCore.getRegisteredInstanceTypeOptional("immutableProofComponent");
226
+ if (!core.Is.empty(defaultImmutableProofComponentType)) {
227
+ await addVerificationMethod(engineCore, context, "immutable proof", envVars.immutableProofVerificationMethodId ?? IMMUTABLE_PROOF_VERIFICATION_METHOD_ID);
228
+ }
214
229
  if (core.Coerce.boolean(envVars.vcAuthenticationEnabled) ?? false) {
215
- await addVerificationMethod(engineCore, context, "verifiable credential authentication", envVars.vcAuthenticationVerificationMethodId);
230
+ await addVerificationMethod(engineCore, context, "verifiable credential authentication", envVars.vcAuthenticationVerificationMethodId ?? VC_AUTHENTICATION_VERIFICATION_METHOD_ID);
216
231
  }
217
232
  await bootstrapSynchronisedStorage(engineCore, context, envVars);
218
233
  }
@@ -488,7 +503,7 @@ async function bootstrapBlobEncryption(engineCore, context, envVars, features) {
488
503
  // Create a new key for encrypting blobs
489
504
  const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
490
505
  const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
491
- const keyName = `${context.state.nodeIdentity}/${envVars.blobStorageEncryptionKeyId}`;
506
+ const keyName = `${context.state.nodeIdentity}/${envVars.blobStorageEncryptionKeyId ?? BLOB_STORAGE_ENCRYPTION_KEY_ID}`;
492
507
  let existingKey;
493
508
  try {
494
509
  existingKey = await vaultConnector.getKey(keyName);
@@ -528,7 +543,7 @@ async function bootstrapAuth(engineCore, context, envVars, features) {
528
543
  // Create a new JWT signing key and a user login for the node
529
544
  const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
530
545
  const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
531
- const keyName = `${context.state.nodeIdentity}/${envVars.authSigningKeyId}`;
546
+ const keyName = `${context.state.nodeIdentity}/${envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID}`;
532
547
  let existingKey;
533
548
  try {
534
549
  existingKey = await vaultConnector.getKey(keyName);
@@ -553,11 +568,11 @@ async function bootstrapAuth(engineCore, context, envVars, features) {
553
568
  async function bootstrapSynchronisedStorage(engineCore, context, envVars, features) {
554
569
  if (core.Coerce.boolean(envVars.synchronisedStorageEnabled) ?? false) {
555
570
  // If this is a trusted node we need to add the blob encryption key pair
556
- if (core.Is.stringValue(envVars.synchronisedStorageBlobStorageEncryptionKeyId) &&
557
- core.Is.stringBase64(envVars.synchronisedStorageBlobStorageKey)) {
571
+ if (core.Is.stringBase64(envVars.synchronisedStorageBlobStorageKey)) {
558
572
  const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
559
573
  const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
560
- const keyName = envVars.synchronisedStorageBlobStorageEncryptionKeyId;
574
+ const keyName = envVars.synchronisedStorageBlobStorageEncryptionKeyId ??
575
+ SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID;
561
576
  let existingKey;
562
577
  try {
563
578
  existingKey = await vaultConnector.getKey(keyName);
@@ -625,12 +640,6 @@ async function buildEngineConfiguration(envVars) {
625
640
  envVars.storageFileRoot = path.resolve(envVars.storageFileRoot);
626
641
  envVars.stateFilename = path.join(envVars.storageFileRoot, envVars.stateFilename);
627
642
  }
628
- envVars.attestationVerificationMethodId ??= "attestation-assertion";
629
- envVars.immutableProofVerificationMethodId ??= "immutable-proof-assertion";
630
- envVars.blobStorageEnableEncryption ??= "false";
631
- envVars.blobStorageEncryptionKeyId ??= "blob-encryption";
632
- envVars.synchronisedStorageBlobStorageEncryptionKeyId ??= "synchronised-storage-blob-encryption";
633
- envVars.vcAuthenticationVerificationMethodId ??= "node-authentication-assertion";
634
643
  const coreConfig = {
635
644
  debug: core.Coerce.boolean(envVars.debug) ?? false,
636
645
  types: {}
@@ -693,9 +702,10 @@ async function configureEntityStorage(coreConfig, envVars) {
693
702
  options: {
694
703
  config: {
695
704
  region: envVars.awsDynamodbRegion ?? "",
696
- accessKeyId: envVars.awsDynamodbAccessKeyId ?? "",
697
- secretAccessKey: envVars.awsDynamodbSecretAccessKey ?? "",
698
- endpoint: envVars.awsDynamodbEndpoint ?? ""
705
+ authMode: envVars.awsDynamodbAuthMode,
706
+ accessKeyId: envVars.awsDynamodbAccessKeyId,
707
+ secretAccessKey: envVars.awsDynamodbSecretAccessKey,
708
+ endpoint: envVars.awsDynamodbEndpoint
699
709
  },
700
710
  tablePrefix: envVars.entityStorageTablePrefix
701
711
  }
@@ -852,9 +862,10 @@ async function configureBlobStorage(coreConfig, envVars) {
852
862
  config: {
853
863
  region: envVars.awsS3Region ?? "",
854
864
  bucketName: envVars.awsS3BucketName ?? "",
855
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
856
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
857
- endpoint: envVars.awsS3Endpoint ?? ""
865
+ authMode: envVars.awsS3AuthMode,
866
+ accessKeyId: envVars.awsS3AccessKeyId,
867
+ secretAccessKey: envVars.awsS3SecretAccessKey,
868
+ endpoint: envVars.awsS3Endpoint
858
869
  },
859
870
  storagePrefix: envVars.blobStoragePrefix
860
871
  }
@@ -911,7 +922,7 @@ async function configureBlobStorage(coreConfig, envVars) {
911
922
  options: {
912
923
  config: {
913
924
  vaultKeyId: (envVars.blobStorageEnableEncryption ?? false)
914
- ? envVars.blobStorageEncryptionKeyId
925
+ ? (envVars.blobStorageEncryptionKeyId ?? BLOB_STORAGE_ENCRYPTION_KEY_ID)
915
926
  : undefined
916
927
  }
917
928
  }
@@ -1051,10 +1062,11 @@ async function configureMessaging(coreConfig, envVars) {
1051
1062
  type: engineTypes.MessagingEmailConnectorType.Aws,
1052
1063
  options: {
1053
1064
  config: {
1054
- region: envVars.awsS3Region ?? "",
1055
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
1056
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
1057
- endpoint: envVars.awsS3Endpoint ?? ""
1065
+ region: envVars.awsSesRegion ?? "",
1066
+ authMode: envVars.awsSesAuthMode,
1067
+ accessKeyId: envVars.awsSesAccessKeyId,
1068
+ secretAccessKey: envVars.awsSesSecretAccessKey,
1069
+ endpoint: envVars.awsSesEndpoint
1058
1070
  }
1059
1071
  }
1060
1072
  });
@@ -1069,10 +1081,11 @@ async function configureMessaging(coreConfig, envVars) {
1069
1081
  type: engineTypes.MessagingSmsConnectorType.Aws,
1070
1082
  options: {
1071
1083
  config: {
1072
- region: envVars.awsS3Region ?? "",
1073
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
1074
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
1075
- endpoint: envVars.awsS3Endpoint ?? ""
1084
+ region: envVars.awsSesRegion ?? "",
1085
+ authMode: envVars.awsSesAuthMode,
1086
+ accessKeyId: envVars.awsSesAccessKeyId,
1087
+ secretAccessKey: envVars.awsSesSecretAccessKey,
1088
+ endpoint: envVars.awsSesEndpoint
1076
1089
  }
1077
1090
  }
1078
1091
  });
@@ -1089,8 +1102,9 @@ async function configureMessaging(coreConfig, envVars) {
1089
1102
  options: {
1090
1103
  config: {
1091
1104
  region: envVars.awsSesRegion ?? "",
1092
- accessKeyId: envVars.awsSesAccessKeyId ?? "",
1093
- secretAccessKey: envVars.awsSesSecretAccessKey ?? "",
1105
+ authMode: envVars.awsSesAuthMode,
1106
+ accessKeyId: envVars.awsSesAccessKeyId,
1107
+ secretAccessKey: envVars.awsSesSecretAccessKey,
1094
1108
  endpoint: envVars.awsSesEndpoint,
1095
1109
  applicationsSettings: core.Is.json(envVars.awsMessagingPushNotificationApplications)
1096
1110
  ? JSON.parse(envVars.awsMessagingPushNotificationApplications)
@@ -1220,7 +1234,7 @@ async function configureVerifiableStorage(coreConfig, envVars) {
1220
1234
  type: engineTypes.ImmutableProofComponentType.Service,
1221
1235
  options: {
1222
1236
  config: {
1223
- verificationMethodId: envVars.immutableProofVerificationMethodId
1237
+ verificationMethodId: envVars.immutableProofVerificationMethodId ?? IMMUTABLE_PROOF_VERIFICATION_METHOD_ID
1224
1238
  }
1225
1239
  }
1226
1240
  });
@@ -1325,7 +1339,7 @@ async function configureAttestation(coreConfig, envVars) {
1325
1339
  type: engineTypes.AttestationComponentType.Service,
1326
1340
  options: {
1327
1341
  config: {
1328
- verificationMethodId: envVars.attestationVerificationMethodId
1342
+ verificationMethodId: envVars.attestationVerificationMethodId ?? ATTESTATION_VERIFICATION_METHOD_ID
1329
1343
  }
1330
1344
  }
1331
1345
  });
@@ -1416,7 +1430,9 @@ async function configureVerifiableCredentialAuthentication(coreConfig, envVars)
1416
1430
  coreConfig.types.authenticationGeneratorComponent.push({
1417
1431
  type: engineTypes.AuthenticationGeneratorComponentType.VerifiableCredential,
1418
1432
  options: {
1419
- config: { verificationMethodId: envVars.vcAuthenticationVerificationMethodId ?? "" }
1433
+ config: {
1434
+ verificationMethodId: envVars.vcAuthenticationVerificationMethodId ?? VC_AUTHENTICATION_VERIFICATION_METHOD_ID
1435
+ }
1420
1436
  },
1421
1437
  features: ["verifiable-credential"]
1422
1438
  });
@@ -1544,7 +1560,8 @@ async function configureSynchronisedStorage(coreConfig, envVars) {
1544
1560
  options: {
1545
1561
  config: {
1546
1562
  verifiableStorageKeyId: verifiableStorageKeyId ?? "",
1547
- blobStorageEncryptionKeyId: envVars.synchronisedStorageBlobStorageEncryptionKeyId,
1563
+ blobStorageEncryptionKeyId: envVars.synchronisedStorageBlobStorageEncryptionKeyId ??
1564
+ SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID,
1548
1565
  entityUpdateIntervalMinutes: core.Coerce.number(envVars.synchronisedStorageEntityUpdateIntervalMinutes),
1549
1566
  consolidationIntervalMinutes: core.Coerce.number(envVars.synchronisedStorageConsolidationIntervalMinutes),
1550
1567
  consolidationBatchSize: core.Coerce.number(envVars.synchronisedStorageConsolidationBatchSize),
@@ -1597,9 +1614,8 @@ async function configureDataSpaceConnector(coreConfig, envVars) {
1597
1614
  type: engineTypes.DataSpaceConnectorComponentType.Service,
1598
1615
  options: {
1599
1616
  config: {
1600
- dataSpaceConnectorAppDescriptors: core.Is.arrayValue(envVars.dataSpaceConnectorApps)
1601
- ? envVars.dataSpaceConnectorApps
1602
- : undefined
1617
+ retainActivityLogsFor: core.Coerce.number(envVars.dataSpaceConnectorRetainActivityLogsFor),
1618
+ activityLogsCleanUpInterval: core.Coerce.number(envVars.dataSpaceConnectorActivityLogsCleanUpInterval)
1603
1619
  }
1604
1620
  }
1605
1621
  });
@@ -1645,10 +1661,9 @@ async function configureDlt(coreConfig, envVars) {
1645
1661
  * @param serverInfo The server information.
1646
1662
  * @param openApiSpecPath The path to the open api spec.
1647
1663
  * @param favIconPath The path to the favicon.
1648
- * @returns The the config for the core and the server.
1664
+ * @returns The config for the core and the server.
1649
1665
  */
1650
1666
  async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, openApiSpecPath, favIconPath) {
1651
- envVars.authSigningKeyId ??= "auth-signing";
1652
1667
  const webServerOptions = {
1653
1668
  port: core.Coerce.number(envVars.port),
1654
1669
  host: core.Coerce.string(envVars.host),
@@ -1764,7 +1779,7 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1764
1779
  type: engineServerTypes.AuthenticationComponentType.EntityStorage,
1765
1780
  options: {
1766
1781
  config: {
1767
- signingKeyName: envVars.authSigningKeyId
1782
+ signingKeyName: envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID
1768
1783
  }
1769
1784
  }
1770
1785
  });
@@ -1772,7 +1787,7 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1772
1787
  type: engineServerTypes.RestRouteProcessorType.AuthHeader,
1773
1788
  options: {
1774
1789
  config: {
1775
- signingKeyName: envVars.authSigningKeyId
1790
+ signingKeyName: envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID
1776
1791
  }
1777
1792
  }
1778
1793
  });
@@ -1780,7 +1795,7 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1780
1795
  type: engineServerTypes.SocketRouteProcessorType.AuthHeader,
1781
1796
  options: {
1782
1797
  config: {
1783
- signingKeyName: envVars.authSigningKeyId
1798
+ signingKeyName: envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID
1784
1799
  }
1785
1800
  }
1786
1801
  });
@@ -1800,15 +1815,111 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1800
1815
 
1801
1816
  // Copyright 2024 IOTA Stiftung.
1802
1817
  // SPDX-License-Identifier: Apache-2.0.
1803
- /* eslint-disable no-console */
1818
+ /**
1819
+ * Handles the configuration of the extensions.
1820
+ * @param envVars The environment variables for the node.
1821
+ * @param nodeEngineConfig The node engine config.
1822
+ * @returns The config for the core and the server.
1823
+ */
1824
+ async function extensionsConfiguration(envVars, nodeEngineConfig) {
1825
+ if (core.Is.stringValue(envVars.extensions)) {
1826
+ const extensions = envVars.extensions.split(",");
1827
+ for (const extension of extensions) {
1828
+ let initialiseConfigMethod;
1829
+ try {
1830
+ cliCore.CLIDisplay.value(core.I18n.formatMessage("node.extensionLoading"), extension);
1831
+ initialiseConfigMethod = await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialise");
1832
+ }
1833
+ catch (err) {
1834
+ throw new core.GeneralError("node", "extensionLoadingError", { extension }, err);
1835
+ }
1836
+ if (core.Is.function(initialiseConfigMethod)) {
1837
+ await initialiseConfigMethod(envVars, nodeEngineConfig);
1838
+ }
1839
+ }
1840
+ }
1841
+ return nodeEngineConfig;
1842
+ }
1843
+ /**
1844
+ * Handles the initialisation of the extensions when the engine has been constructed.
1845
+ * @param envVars The environment variables for the node.
1846
+ * @param engineCore The engine core instance.
1847
+ * @returns Nothing.
1848
+ */
1849
+ async function extensionsInitialiseEngine(envVars, engineCore) {
1850
+ if (core.Is.stringValue(envVars.extensions)) {
1851
+ const extensions = envVars.extensions.split(",");
1852
+ for (const extension of extensions) {
1853
+ let initialiseEngineMethod;
1854
+ try {
1855
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionInitialisingEngine", { extension }));
1856
+ initialiseEngineMethod =
1857
+ await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngine");
1858
+ }
1859
+ catch { }
1860
+ if (core.Is.function(initialiseEngineMethod)) {
1861
+ await initialiseEngineMethod(engineCore);
1862
+ }
1863
+ }
1864
+ }
1865
+ }
1866
+ /**
1867
+ * Handles the initialisation of the extensions when the engine server has been constructed.
1868
+ * @param envVars The environment variables for the node.
1869
+ * @param engineCore The engine core instance.
1870
+ * @param engineServer The engine server instance.
1871
+ * @returns Nothing.
1872
+ */
1873
+ async function extensionsInitialiseEngineServer(envVars, engineCore, engineServer) {
1874
+ if (core.Is.stringValue(envVars.extensions)) {
1875
+ const extensions = envVars.extensions.split(",");
1876
+ for (const extension of extensions) {
1877
+ let initialiseEngineServerMethod;
1878
+ try {
1879
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionInitialisingEngineServer", { extension }));
1880
+ initialiseEngineServerMethod =
1881
+ await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngineServer");
1882
+ }
1883
+ catch { }
1884
+ if (core.Is.function(initialiseEngineServerMethod)) {
1885
+ await initialiseEngineServerMethod(engineCore, engineServer);
1886
+ }
1887
+ }
1888
+ }
1889
+ }
1890
+ /**
1891
+ * Handles the shutdown of the extensions.
1892
+ * @param envVars The environment variables for the node.
1893
+ * @param engineCore The engine core instance.
1894
+ * @returns Nothing.
1895
+ */
1896
+ async function shutdownExtensions(envVars, engineCore) {
1897
+ if (core.Is.stringValue(envVars.extensions)) {
1898
+ const extensions = envVars.extensions.split(",");
1899
+ for (const extension of extensions) {
1900
+ let shutdownMethod;
1901
+ try {
1902
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionShutdown", { extension }));
1903
+ shutdownMethod = await modules.ModuleHelper.getModuleMethod(extension, "extensionShutdown");
1904
+ }
1905
+ catch { }
1906
+ if (core.Is.function(shutdownMethod)) {
1907
+ await shutdownMethod();
1908
+ }
1909
+ }
1910
+ }
1911
+ }
1912
+
1913
+ // Copyright 2024 IOTA Stiftung.
1914
+ // SPDX-License-Identifier: Apache-2.0.
1804
1915
  /**
1805
1916
  * Start the engine server.
1806
1917
  * @param nodeOptions Optional run options for the engine server.
1807
- * @param engineServerConfig The configuration for the engine server.
1918
+ * @param nodeEngineConfig The configuration for the engine server.
1808
1919
  * @param envVars The environment variables.
1809
1920
  * @returns The engine server.
1810
1921
  */
1811
- async function start(nodeOptions, engineServerConfig, envVars) {
1922
+ async function start(nodeOptions, nodeEngineConfig, envVars) {
1812
1923
  const entityStorageConnectorType = envVars.entityStorageConnectorType?.split(",") ?? [];
1813
1924
  const blobStorageConnectorType = envVars.blobStorageConnectorType?.split(",") ?? [];
1814
1925
  // If the blob storage or entity storage is configured with file connectors
@@ -1823,7 +1934,7 @@ async function start(nodeOptions, engineServerConfig, envVars) {
1823
1934
  }
1824
1935
  // Create the engine instance using file state storage unless one is configured in options
1825
1936
  const engine$1 = new engine.Engine({
1826
- config: engineServerConfig,
1937
+ config: nodeEngineConfig,
1827
1938
  stateStorage: nodeOptions?.stateStorage ?? new engineCore.FileStateStorage(envVars.stateFilename ?? ""),
1828
1939
  customBootstrap: async (core, engineContext) => bootstrap(core, engineContext, envVars)
1829
1940
  });
@@ -1831,14 +1942,16 @@ async function start(nodeOptions, engineServerConfig, envVars) {
1831
1942
  const server = new engineServer.EngineServer({ engineCore: engine$1 });
1832
1943
  // Extend the engine.
1833
1944
  if (core.Is.function(nodeOptions?.extendEngine)) {
1834
- console.info("Extending Engine");
1945
+ engine$1.logInfo(core.I18n.formatMessage("node.extendingEngine"));
1835
1946
  await nodeOptions.extendEngine(engine$1);
1836
1947
  }
1948
+ await extensionsInitialiseEngine(envVars, engine$1);
1837
1949
  // Extend the engine server.
1838
1950
  if (core.Is.function(nodeOptions?.extendEngineServer)) {
1839
- console.info("Extending Engine Server");
1951
+ engine$1.logInfo(core.I18n.formatMessage("node.extendingEngineServer"));
1840
1952
  await nodeOptions?.extendEngineServer(server);
1841
1953
  }
1954
+ await extensionsInitialiseEngineServer(envVars, engine$1, server);
1842
1955
  // Need to register the engine with the factory so that background tasks
1843
1956
  // can clone it to spawn new instances.
1844
1957
  engineModels.EngineCoreFactory.register("engine", () => engine$1);
@@ -1847,14 +1960,17 @@ async function start(nodeOptions, engineServerConfig, envVars) {
1847
1960
  if (canContinue) {
1848
1961
  return {
1849
1962
  engine: engine$1,
1850
- server
1963
+ server,
1964
+ shutdown: async () => {
1965
+ await server.stop();
1966
+ await shutdownExtensions(envVars, engine$1);
1967
+ }
1851
1968
  };
1852
1969
  }
1853
1970
  }
1854
1971
 
1855
1972
  // Copyright 2024 IOTA Stiftung.
1856
1973
  // SPDX-License-Identifier: Apache-2.0.
1857
- /* eslint-disable no-console */
1858
1974
  /**
1859
1975
  * Run the TWIN Node server.
1860
1976
  * @param nodeOptions Optional configuration options for running the server.
@@ -1865,50 +1981,59 @@ async function run(nodeOptions) {
1865
1981
  nodeOptions ??= {};
1866
1982
  const serverInfo = {
1867
1983
  name: nodeOptions?.serverName ?? "TWIN Node Server",
1868
- version: nodeOptions?.serverVersion ?? "0.0.2-next.16" // x-release-please-version
1984
+ version: nodeOptions?.serverVersion ?? "0.0.2-next.18" // x-release-please-version
1869
1985
  };
1870
- console.log(`\u001B[4m🌩️ ${serverInfo.name} v${serverInfo.version}\u001B[24m\n`);
1986
+ cliCore.CLIDisplay.header(serverInfo.name, serverInfo.version, "🌩️ ");
1871
1987
  if (!core.Is.stringValue(nodeOptions?.executionDirectory)) {
1872
1988
  nodeOptions.executionDirectory = getExecutionDirectory();
1873
1989
  }
1874
- console.info("Execution Directory:", nodeOptions.executionDirectory);
1990
+ cliCore.CLIDisplay.value("Execution Directory", nodeOptions.executionDirectory);
1875
1991
  nodeOptions.localesDirectory =
1876
1992
  nodeOptions?.localesDirectory ??
1877
1993
  path.resolve(path.join(nodeOptions.executionDirectory, "dist", "locales"));
1878
- console.info("Locales Directory:", nodeOptions.localesDirectory);
1994
+ cliCore.CLIDisplay.value("Locales Directory", nodeOptions.localesDirectory);
1879
1995
  await initialiseLocales(nodeOptions.localesDirectory);
1880
1996
  if (core.Is.empty(nodeOptions?.openApiSpecFile)) {
1881
1997
  const specFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "docs", "open-api", "spec.json"));
1882
- console.info("Default OpenAPI Spec File:", specFile);
1998
+ cliCore.CLIDisplay.value("Default OpenAPI Spec File", specFile);
1883
1999
  if (await fileExists(specFile)) {
1884
2000
  nodeOptions ??= {};
1885
2001
  nodeOptions.openApiSpecFile = specFile;
1886
2002
  }
1887
2003
  }
2004
+ else {
2005
+ cliCore.CLIDisplay.value("OpenAPI Spec File", nodeOptions.openApiSpecFile);
2006
+ }
1888
2007
  if (core.Is.empty(nodeOptions?.favIconFile)) {
1889
2008
  const favIconFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "static", "favicon.png"));
1890
- console.info("Default Favicon File:", favIconFile);
2009
+ cliCore.CLIDisplay.value("Default Favicon File", favIconFile);
1891
2010
  if (await fileExists(favIconFile)) {
1892
2011
  nodeOptions ??= {};
1893
2012
  nodeOptions.favIconFile = favIconFile;
1894
2013
  }
1895
2014
  }
2015
+ else {
2016
+ cliCore.CLIDisplay.value("Favicon File", nodeOptions.favIconFile);
2017
+ }
1896
2018
  nodeOptions.envPrefix ??= "TWIN_NODE_";
1897
- console.info("Environment Prefix:", nodeOptions.envPrefix);
2019
+ cliCore.CLIDisplay.value("Environment Variable Prefix", nodeOptions.envPrefix);
1898
2020
  overrideModuleImport(nodeOptions.executionDirectory ?? "");
1899
- const { engineServerConfig, nodeEnvVars: envVars } = await buildConfiguration(process.env, nodeOptions, serverInfo);
1900
- console.info();
1901
- const startResult = await start(nodeOptions, engineServerConfig, envVars);
2021
+ const { nodeEngineConfig, nodeEnvVars: envVars } = await buildConfiguration(
2022
+ // This is the only location in the code base that should access process.env directly
2023
+ // eslint-disable-next-line no-restricted-syntax
2024
+ process.env, nodeOptions, serverInfo);
2025
+ cliCore.CLIDisplay.break();
2026
+ const startResult = await start(nodeOptions, nodeEngineConfig, envVars);
1902
2027
  if (!core.Is.empty(startResult)) {
1903
2028
  for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
1904
2029
  process.on(signal, async () => {
1905
- await startResult.server.stop();
2030
+ await startResult.shutdown();
1906
2031
  });
1907
2032
  }
1908
2033
  }
1909
2034
  }
1910
2035
  catch (err) {
1911
- console.error(core.ErrorHelper.formatErrors(err).join("\n"));
2036
+ cliCore.CLIDisplay.error(err);
1912
2037
  // eslint-disable-next-line unicorn/no-process-exit
1913
2038
  process.exit(1);
1914
2039
  }
@@ -1925,7 +2050,7 @@ async function buildConfiguration(processEnv, options, serverInfo) {
1925
2050
  let defaultEnvOnly = false;
1926
2051
  if (core.Is.empty(options?.envFilenames)) {
1927
2052
  const envFile = path.resolve(path.join(options.executionDirectory ?? "", ".env"));
1928
- console.info("Default Environment File:", envFile);
2053
+ cliCore.CLIDisplay.value("Default Environment File", envFile);
1929
2054
  options ??= {};
1930
2055
  options.envFilenames = [envFile];
1931
2056
  defaultEnvOnly = true;
@@ -1953,18 +2078,18 @@ async function buildConfiguration(processEnv, options, serverInfo) {
1953
2078
  const filePath = envVars[key].slice(6).trim();
1954
2079
  const embeddedFile = path.resolve(path.join(options.executionDirectory ?? "", filePath));
1955
2080
  if (envVars[key].startsWith("@text:")) {
1956
- console.info(`Expanding Environment Variable: ${key} from text file: ${embeddedFile}`);
2081
+ cliCore.CLIDisplay.value(`Expanding Environment Variable: ${key} from text file`, embeddedFile);
1957
2082
  envVars[key] = await loadTextFile(embeddedFile);
1958
2083
  }
1959
2084
  else if (envVars[key].startsWith("@json:")) {
1960
- console.info(`Expanding Environment Variable: ${key} from JSON file: ${embeddedFile}`);
2085
+ cliCore.CLIDisplay.value(`Expanding Environment Variable: ${key} from JSON file`, embeddedFile);
1961
2086
  envVars[key] = await loadJsonFile(embeddedFile);
1962
2087
  }
1963
2088
  }
1964
2089
  }
1965
2090
  // Extend the environment variables with any additional custom configuration.
1966
2091
  if (core.Is.function(options?.extendEnvVars)) {
1967
- console.info("Extending Environment Variables");
2092
+ cliCore.CLIDisplay.task("Extending Environment Variables");
1968
2093
  await options.extendEnvVars(envVars);
1969
2094
  }
1970
2095
  // Build the engine configuration from the environment variables.
@@ -1973,22 +2098,23 @@ async function buildConfiguration(processEnv, options, serverInfo) {
1973
2098
  // Merge any custom configuration provided in the options.
1974
2099
  if (core.Is.arrayValue(options?.configFilenames)) {
1975
2100
  for (const configFile of options.configFilenames) {
1976
- console.info("Loading Configuration File:", configFile);
2101
+ cliCore.CLIDisplay.value("Loading Configuration File", configFile);
1977
2102
  const configFilePath = path.resolve(path.join(options.executionDirectory ?? "", configFile));
1978
2103
  const config = await loadJsonFile(configFilePath);
1979
2104
  Object.assign(engineServerConfig, config);
1980
2105
  }
1981
2106
  }
1982
2107
  if (core.Is.objectValue(options?.config)) {
1983
- console.info("Merging Custom Configuration");
2108
+ cliCore.CLIDisplay.task("Merging Custom Configuration");
1984
2109
  Object.assign(engineServerConfig, options.config);
1985
2110
  }
1986
2111
  // Merge any custom configuration provided in the options.
1987
2112
  if (core.Is.function(options?.extendConfig)) {
1988
- console.info("Extending Configuration");
1989
- await options.extendConfig(engineServerConfig);
2113
+ cliCore.CLIDisplay.task("Extending Configuration");
2114
+ await options.extendConfig(envVars, engineServerConfig);
1990
2115
  }
1991
- return { engineServerConfig, nodeEnvVars: envVars };
2116
+ const nodeEngineConfig = await extensionsConfiguration(envVars, engineServerConfig);
2117
+ return { nodeEngineConfig, nodeEnvVars: envVars };
1992
2118
  }
1993
2119
  /**
1994
2120
  * Override module imports to use local files where possible.
@@ -2025,7 +2151,13 @@ function overrideModuleImport(executionDirectory) {
2025
2151
  });
2026
2152
  }
2027
2153
 
2154
+ exports.ATTESTATION_VERIFICATION_METHOD_ID = ATTESTATION_VERIFICATION_METHOD_ID;
2155
+ exports.AUTH_SIGNING_KEY_ID = AUTH_SIGNING_KEY_ID;
2156
+ exports.BLOB_STORAGE_ENCRYPTION_KEY_ID = BLOB_STORAGE_ENCRYPTION_KEY_ID;
2157
+ exports.IMMUTABLE_PROOF_VERIFICATION_METHOD_ID = IMMUTABLE_PROOF_VERIFICATION_METHOD_ID;
2028
2158
  exports.NodeFeatures = NodeFeatures;
2159
+ exports.SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID = SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID;
2160
+ exports.VC_AUTHENTICATION_VERIFICATION_METHOD_ID = VC_AUTHENTICATION_VERIFICATION_METHOD_ID;
2029
2161
  exports.bootstrap = bootstrap;
2030
2162
  exports.bootstrapAuth = bootstrapAuth;
2031
2163
  exports.bootstrapBlobEncryption = bootstrapBlobEncryption;
@@ -2037,6 +2169,9 @@ exports.buildConfiguration = buildConfiguration;
2037
2169
  exports.buildEngineConfiguration = buildEngineConfiguration;
2038
2170
  exports.buildEngineServerConfiguration = buildEngineServerConfiguration;
2039
2171
  exports.directoryExists = directoryExists;
2172
+ exports.extensionsConfiguration = extensionsConfiguration;
2173
+ exports.extensionsInitialiseEngine = extensionsInitialiseEngine;
2174
+ exports.extensionsInitialiseEngineServer = extensionsInitialiseEngineServer;
2040
2175
  exports.fileExists = fileExists;
2041
2176
  exports.getExecutionDirectory = getExecutionDirectory;
2042
2177
  exports.getFeatures = getFeatures;
@@ -2047,4 +2182,5 @@ exports.loadJsonFile = loadJsonFile;
2047
2182
  exports.loadTextFile = loadTextFile;
2048
2183
  exports.overrideModuleImport = overrideModuleImport;
2049
2184
  exports.run = run;
2185
+ exports.shutdownExtensions = shutdownExtensions;
2050
2186
  exports.start = start;