@twin.org/node-core 0.0.2-next.17 → 0.0.2-next.19

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.
@@ -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');
@@ -70,20 +71,19 @@ const NodeFeatures = {
70
71
 
71
72
  // Copyright 2024 IOTA Stiftung.
72
73
  // SPDX-License-Identifier: Apache-2.0.
73
- /* eslint-disable no-console */
74
74
  /**
75
75
  * Initialise the locales for the application.
76
76
  * @param localesDirectory The directory containing the locales.
77
77
  */
78
78
  async function initialiseLocales(localesDirectory) {
79
79
  const localesFile = path.resolve(path.join(localesDirectory, "en.json"));
80
- console.info("Locales File:", localesFile);
80
+ cliCore.CLIDisplay.value("Locales File", localesFile);
81
81
  if (await fileExists(localesFile)) {
82
82
  const enLangContent = await promises.readFile(localesFile, "utf8");
83
83
  core.I18n.addDictionary("en", JSON.parse(enLangContent));
84
84
  }
85
85
  else {
86
- console.warn(`Locales file not found: ${localesFile}`);
86
+ cliCore.CLIDisplay.error(`Locales file not found: ${localesFile}`);
87
87
  }
88
88
  }
89
89
  /**
@@ -1710,8 +1710,9 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1710
1710
  }
1711
1711
  serverConfig.types.restRouteProcessor ??= [];
1712
1712
  serverConfig.types.socketRouteProcessor ??= [];
1713
- const disableNodeIdentity = core.Coerce.boolean(envVars.disableNodeIdentity);
1714
- if (!disableNodeIdentity) {
1713
+ const features = getFeatures(envVars);
1714
+ const hasNodeIdentity = features.includes(NodeFeatures.NodeIdentity);
1715
+ if (hasNodeIdentity) {
1715
1716
  serverConfig.types.restRouteProcessor.push({
1716
1717
  type: engineServerTypes.RestRouteProcessorType.NodeIdentity
1717
1718
  });
@@ -1815,7 +1816,6 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1815
1816
 
1816
1817
  // Copyright 2024 IOTA Stiftung.
1817
1818
  // SPDX-License-Identifier: Apache-2.0.
1818
- /* eslint-disable no-console */
1819
1819
  /**
1820
1820
  * Handles the configuration of the extensions.
1821
1821
  * @param envVars The environment variables for the node.
@@ -1828,11 +1828,11 @@ async function extensionsConfiguration(envVars, nodeEngineConfig) {
1828
1828
  for (const extension of extensions) {
1829
1829
  let initialiseConfigMethod;
1830
1830
  try {
1831
- console.info(`Loading extension "${extension}"`);
1831
+ cliCore.CLIDisplay.value(core.I18n.formatMessage("node.extensionLoading"), extension);
1832
1832
  initialiseConfigMethod = await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialise");
1833
1833
  }
1834
1834
  catch (err) {
1835
- console.error(`Failed to load extension "${extension}":`, err);
1835
+ throw new core.GeneralError("node", "extensionLoadingError", { extension }, err);
1836
1836
  }
1837
1837
  if (core.Is.function(initialiseConfigMethod)) {
1838
1838
  await initialiseConfigMethod(envVars, nodeEngineConfig);
@@ -1853,6 +1853,7 @@ async function extensionsInitialiseEngine(envVars, engineCore) {
1853
1853
  for (const extension of extensions) {
1854
1854
  let initialiseEngineMethod;
1855
1855
  try {
1856
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionInitialisingEngine", { extension }));
1856
1857
  initialiseEngineMethod =
1857
1858
  await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngine");
1858
1859
  }
@@ -1876,6 +1877,7 @@ async function extensionsInitialiseEngineServer(envVars, engineCore, engineServe
1876
1877
  for (const extension of extensions) {
1877
1878
  let initialiseEngineServerMethod;
1878
1879
  try {
1880
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionInitialisingEngineServer", { extension }));
1879
1881
  initialiseEngineServerMethod =
1880
1882
  await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngineServer");
1881
1883
  }
@@ -1889,14 +1891,16 @@ async function extensionsInitialiseEngineServer(envVars, engineCore, engineServe
1889
1891
  /**
1890
1892
  * Handles the shutdown of the extensions.
1891
1893
  * @param envVars The environment variables for the node.
1894
+ * @param engineCore The engine core instance.
1892
1895
  * @returns Nothing.
1893
1896
  */
1894
- async function shutdownExtensions(envVars) {
1897
+ async function shutdownExtensions(envVars, engineCore) {
1895
1898
  if (core.Is.stringValue(envVars.extensions)) {
1896
1899
  const extensions = envVars.extensions.split(",");
1897
1900
  for (const extension of extensions) {
1898
1901
  let shutdownMethod;
1899
1902
  try {
1903
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionShutdown", { extension }));
1900
1904
  shutdownMethod = await modules.ModuleHelper.getModuleMethod(extension, "extensionShutdown");
1901
1905
  }
1902
1906
  catch { }
@@ -1909,7 +1913,6 @@ async function shutdownExtensions(envVars) {
1909
1913
 
1910
1914
  // Copyright 2024 IOTA Stiftung.
1911
1915
  // SPDX-License-Identifier: Apache-2.0.
1912
- /* eslint-disable no-console */
1913
1916
  /**
1914
1917
  * Start the engine server.
1915
1918
  * @param nodeOptions Optional run options for the engine server.
@@ -1940,13 +1943,13 @@ async function start(nodeOptions, nodeEngineConfig, envVars) {
1940
1943
  const server = new engineServer.EngineServer({ engineCore: engine$1 });
1941
1944
  // Extend the engine.
1942
1945
  if (core.Is.function(nodeOptions?.extendEngine)) {
1943
- console.info("Extending Engine");
1946
+ engine$1.logInfo(core.I18n.formatMessage("node.extendingEngine"));
1944
1947
  await nodeOptions.extendEngine(engine$1);
1945
1948
  }
1946
1949
  await extensionsInitialiseEngine(envVars, engine$1);
1947
1950
  // Extend the engine server.
1948
1951
  if (core.Is.function(nodeOptions?.extendEngineServer)) {
1949
- console.info("Extending Engine Server");
1952
+ engine$1.logInfo(core.I18n.formatMessage("node.extendingEngineServer"));
1950
1953
  await nodeOptions?.extendEngineServer(server);
1951
1954
  }
1952
1955
  await extensionsInitialiseEngineServer(envVars, engine$1, server);
@@ -1961,7 +1964,7 @@ async function start(nodeOptions, nodeEngineConfig, envVars) {
1961
1964
  server,
1962
1965
  shutdown: async () => {
1963
1966
  await server.stop();
1964
- await shutdownExtensions(envVars);
1967
+ await shutdownExtensions(envVars, engine$1);
1965
1968
  }
1966
1969
  };
1967
1970
  }
@@ -1969,7 +1972,6 @@ async function start(nodeOptions, nodeEngineConfig, envVars) {
1969
1972
 
1970
1973
  // Copyright 2024 IOTA Stiftung.
1971
1974
  // SPDX-License-Identifier: Apache-2.0.
1972
- /* eslint-disable no-console */
1973
1975
  /**
1974
1976
  * Run the TWIN Node server.
1975
1977
  * @param nodeOptions Optional configuration options for running the server.
@@ -1980,42 +1982,48 @@ async function run(nodeOptions) {
1980
1982
  nodeOptions ??= {};
1981
1983
  const serverInfo = {
1982
1984
  name: nodeOptions?.serverName ?? "TWIN Node Server",
1983
- version: nodeOptions?.serverVersion ?? "0.0.2-next.17" // x-release-please-version
1985
+ version: nodeOptions?.serverVersion ?? "0.0.2-next.19" // x-release-please-version
1984
1986
  };
1985
- console.log(`\u001B[4m🌩️ ${serverInfo.name} v${serverInfo.version}\u001B[24m\n`);
1987
+ cliCore.CLIDisplay.header(serverInfo.name, serverInfo.version, "🌩️ ");
1986
1988
  if (!core.Is.stringValue(nodeOptions?.executionDirectory)) {
1987
1989
  nodeOptions.executionDirectory = getExecutionDirectory();
1988
1990
  }
1989
- console.info("Execution Directory:", nodeOptions.executionDirectory);
1991
+ cliCore.CLIDisplay.value("Execution Directory", nodeOptions.executionDirectory);
1990
1992
  nodeOptions.localesDirectory =
1991
1993
  nodeOptions?.localesDirectory ??
1992
1994
  path.resolve(path.join(nodeOptions.executionDirectory, "dist", "locales"));
1993
- console.info("Locales Directory:", nodeOptions.localesDirectory);
1995
+ cliCore.CLIDisplay.value("Locales Directory", nodeOptions.localesDirectory);
1994
1996
  await initialiseLocales(nodeOptions.localesDirectory);
1995
1997
  if (core.Is.empty(nodeOptions?.openApiSpecFile)) {
1996
1998
  const specFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "docs", "open-api", "spec.json"));
1997
- console.info("Default OpenAPI Spec File:", specFile);
1999
+ cliCore.CLIDisplay.value("Default OpenAPI Spec File", specFile);
1998
2000
  if (await fileExists(specFile)) {
1999
2001
  nodeOptions ??= {};
2000
2002
  nodeOptions.openApiSpecFile = specFile;
2001
2003
  }
2002
2004
  }
2005
+ else {
2006
+ cliCore.CLIDisplay.value("OpenAPI Spec File", nodeOptions.openApiSpecFile);
2007
+ }
2003
2008
  if (core.Is.empty(nodeOptions?.favIconFile)) {
2004
2009
  const favIconFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "static", "favicon.png"));
2005
- console.info("Default Favicon File:", favIconFile);
2010
+ cliCore.CLIDisplay.value("Default Favicon File", favIconFile);
2006
2011
  if (await fileExists(favIconFile)) {
2007
2012
  nodeOptions ??= {};
2008
2013
  nodeOptions.favIconFile = favIconFile;
2009
2014
  }
2010
2015
  }
2016
+ else {
2017
+ cliCore.CLIDisplay.value("Favicon File", nodeOptions.favIconFile);
2018
+ }
2011
2019
  nodeOptions.envPrefix ??= "TWIN_NODE_";
2012
- console.info("Environment Prefix:", nodeOptions.envPrefix);
2020
+ cliCore.CLIDisplay.value("Environment Variable Prefix", nodeOptions.envPrefix);
2013
2021
  overrideModuleImport(nodeOptions.executionDirectory ?? "");
2014
2022
  const { nodeEngineConfig, nodeEnvVars: envVars } = await buildConfiguration(
2015
2023
  // This is the only location in the code base that should access process.env directly
2016
2024
  // eslint-disable-next-line no-restricted-syntax
2017
2025
  process.env, nodeOptions, serverInfo);
2018
- console.info();
2026
+ cliCore.CLIDisplay.break();
2019
2027
  const startResult = await start(nodeOptions, nodeEngineConfig, envVars);
2020
2028
  if (!core.Is.empty(startResult)) {
2021
2029
  for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
@@ -2026,7 +2034,7 @@ async function run(nodeOptions) {
2026
2034
  }
2027
2035
  }
2028
2036
  catch (err) {
2029
- console.error(core.ErrorHelper.formatErrors(err).join("\n"));
2037
+ cliCore.CLIDisplay.error(err);
2030
2038
  // eslint-disable-next-line unicorn/no-process-exit
2031
2039
  process.exit(1);
2032
2040
  }
@@ -2043,7 +2051,7 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2043
2051
  let defaultEnvOnly = false;
2044
2052
  if (core.Is.empty(options?.envFilenames)) {
2045
2053
  const envFile = path.resolve(path.join(options.executionDirectory ?? "", ".env"));
2046
- console.info("Default Environment File:", envFile);
2054
+ cliCore.CLIDisplay.value("Default Environment File", envFile);
2047
2055
  options ??= {};
2048
2056
  options.envFilenames = [envFile];
2049
2057
  defaultEnvOnly = true;
@@ -2071,18 +2079,18 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2071
2079
  const filePath = envVars[key].slice(6).trim();
2072
2080
  const embeddedFile = path.resolve(path.join(options.executionDirectory ?? "", filePath));
2073
2081
  if (envVars[key].startsWith("@text:")) {
2074
- console.info(`Expanding Environment Variable: ${key} from text file: ${embeddedFile}`);
2082
+ cliCore.CLIDisplay.value(`Expanding Environment Variable: ${key} from text file`, embeddedFile);
2075
2083
  envVars[key] = await loadTextFile(embeddedFile);
2076
2084
  }
2077
2085
  else if (envVars[key].startsWith("@json:")) {
2078
- console.info(`Expanding Environment Variable: ${key} from JSON file: ${embeddedFile}`);
2086
+ cliCore.CLIDisplay.value(`Expanding Environment Variable: ${key} from JSON file`, embeddedFile);
2079
2087
  envVars[key] = await loadJsonFile(embeddedFile);
2080
2088
  }
2081
2089
  }
2082
2090
  }
2083
2091
  // Extend the environment variables with any additional custom configuration.
2084
2092
  if (core.Is.function(options?.extendEnvVars)) {
2085
- console.info("Extending Environment Variables");
2093
+ cliCore.CLIDisplay.task("Extending Environment Variables");
2086
2094
  await options.extendEnvVars(envVars);
2087
2095
  }
2088
2096
  // Build the engine configuration from the environment variables.
@@ -2091,19 +2099,19 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2091
2099
  // Merge any custom configuration provided in the options.
2092
2100
  if (core.Is.arrayValue(options?.configFilenames)) {
2093
2101
  for (const configFile of options.configFilenames) {
2094
- console.info("Loading Configuration File:", configFile);
2102
+ cliCore.CLIDisplay.value("Loading Configuration File", configFile);
2095
2103
  const configFilePath = path.resolve(path.join(options.executionDirectory ?? "", configFile));
2096
2104
  const config = await loadJsonFile(configFilePath);
2097
2105
  Object.assign(engineServerConfig, config);
2098
2106
  }
2099
2107
  }
2100
2108
  if (core.Is.objectValue(options?.config)) {
2101
- console.info("Merging Custom Configuration");
2109
+ cliCore.CLIDisplay.task("Merging Custom Configuration");
2102
2110
  Object.assign(engineServerConfig, options.config);
2103
2111
  }
2104
2112
  // Merge any custom configuration provided in the options.
2105
2113
  if (core.Is.function(options?.extendConfig)) {
2106
- console.info("Extending Configuration");
2114
+ cliCore.CLIDisplay.task("Extending Configuration");
2107
2115
  await options.extendConfig(envVars, engineServerConfig);
2108
2116
  }
2109
2117
  const nodeEngineConfig = await extensionsConfiguration(envVars, engineServerConfig);
@@ -1,5 +1,5 @@
1
1
  import { PasswordHelper } from '@twin.org/api-auth-entity-storage-service';
2
- import { I18n, Is, Coerce, Converter, RandomHelper, Urn, GeneralError, ErrorHelper, EnvHelper } from '@twin.org/core';
2
+ import { I18n, Is, Coerce, Converter, RandomHelper, Urn, GeneralError, EnvHelper } from '@twin.org/core';
3
3
  import { PasswordGenerator, Bip39 } from '@twin.org/crypto';
4
4
  import { AuthenticationComponentType, InformationComponentType, RestRouteProcessorType, SocketRouteProcessorType, AuthenticationAdminComponentType } from '@twin.org/engine-server-types';
5
5
  import { WalletConnectorType, IdentityConnectorType, EntityStorageConnectorType, BlobStorageConnectorType, BlobStorageComponentType, VaultConnectorType, DltConfigType, LoggingConnectorType, LoggingComponentType, BackgroundTaskConnectorType, TaskSchedulerComponentType, EventBusConnectorType, EventBusComponentType, TelemetryConnectorType, TelemetryComponentType, MessagingEmailConnectorType, MessagingSmsConnectorType, MessagingPushNotificationConnectorType, MessagingAdminComponentType, MessagingComponentType, FaucetConnectorType, EngineTypeHelper, NftConnectorType, NftComponentType, VerifiableStorageConnectorType, VerifiableStorageComponentType, ImmutableProofComponentType, IdentityComponentType, IdentityResolverConnectorType, IdentityResolverComponentType, IdentityProfileConnectorType, IdentityProfileComponentType, AttestationConnectorType, AttestationComponentType, DataProcessingComponentType, DataConverterConnectorType, DataExtractorConnectorType, AuditableItemGraphComponentType, AuditableItemStreamComponentType, DocumentManagementComponentType, AuthenticationGeneratorComponentType, RightsManagementPapComponentType, RightsManagementPmpComponentType, RightsManagementPipComponentType, RightsManagementPxpComponentType, RightsManagementPdpComponentType, RightsManagementPepComponentType, RightsManagementPnpComponentType, RightsManagementPnapComponentType, RightsManagementDapComponentType, RightsManagementDarpComponentType, SynchronisedStorageComponentType, FederatedCatalogueComponentType, DataSpaceConnectorComponentType } from '@twin.org/engine-types';
@@ -9,6 +9,7 @@ import { VaultConnectorFactory, VaultKeyType } from '@twin.org/vault-models';
9
9
  import { WalletConnectorFactory } from '@twin.org/wallet-models';
10
10
  import { readFile, stat, readdir } from 'node:fs/promises';
11
11
  import path from 'node:path';
12
+ import { CLIDisplay } from '@twin.org/cli-core';
12
13
  import { PolicyNegotiationPointClient, DataAccessPointClient } from '@twin.org/rights-management-rest-client';
13
14
  import { addDefaultRestPaths, addDefaultSocketPaths, EngineServer } from '@twin.org/engine-server';
14
15
  import { ModuleHelper } from '@twin.org/modules';
@@ -49,20 +50,19 @@ const NodeFeatures = {
49
50
 
50
51
  // Copyright 2024 IOTA Stiftung.
51
52
  // SPDX-License-Identifier: Apache-2.0.
52
- /* eslint-disable no-console */
53
53
  /**
54
54
  * Initialise the locales for the application.
55
55
  * @param localesDirectory The directory containing the locales.
56
56
  */
57
57
  async function initialiseLocales(localesDirectory) {
58
58
  const localesFile = path.resolve(path.join(localesDirectory, "en.json"));
59
- console.info("Locales File:", localesFile);
59
+ CLIDisplay.value("Locales File", localesFile);
60
60
  if (await fileExists(localesFile)) {
61
61
  const enLangContent = await readFile(localesFile, "utf8");
62
62
  I18n.addDictionary("en", JSON.parse(enLangContent));
63
63
  }
64
64
  else {
65
- console.warn(`Locales file not found: ${localesFile}`);
65
+ CLIDisplay.error(`Locales file not found: ${localesFile}`);
66
66
  }
67
67
  }
68
68
  /**
@@ -1689,8 +1689,9 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1689
1689
  }
1690
1690
  serverConfig.types.restRouteProcessor ??= [];
1691
1691
  serverConfig.types.socketRouteProcessor ??= [];
1692
- const disableNodeIdentity = Coerce.boolean(envVars.disableNodeIdentity);
1693
- if (!disableNodeIdentity) {
1692
+ const features = getFeatures(envVars);
1693
+ const hasNodeIdentity = features.includes(NodeFeatures.NodeIdentity);
1694
+ if (hasNodeIdentity) {
1694
1695
  serverConfig.types.restRouteProcessor.push({
1695
1696
  type: RestRouteProcessorType.NodeIdentity
1696
1697
  });
@@ -1794,7 +1795,6 @@ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverI
1794
1795
 
1795
1796
  // Copyright 2024 IOTA Stiftung.
1796
1797
  // SPDX-License-Identifier: Apache-2.0.
1797
- /* eslint-disable no-console */
1798
1798
  /**
1799
1799
  * Handles the configuration of the extensions.
1800
1800
  * @param envVars The environment variables for the node.
@@ -1807,11 +1807,11 @@ async function extensionsConfiguration(envVars, nodeEngineConfig) {
1807
1807
  for (const extension of extensions) {
1808
1808
  let initialiseConfigMethod;
1809
1809
  try {
1810
- console.info(`Loading extension "${extension}"`);
1810
+ CLIDisplay.value(I18n.formatMessage("node.extensionLoading"), extension);
1811
1811
  initialiseConfigMethod = await ModuleHelper.getModuleMethod(extension, "extensionInitialise");
1812
1812
  }
1813
1813
  catch (err) {
1814
- console.error(`Failed to load extension "${extension}":`, err);
1814
+ throw new GeneralError("node", "extensionLoadingError", { extension }, err);
1815
1815
  }
1816
1816
  if (Is.function(initialiseConfigMethod)) {
1817
1817
  await initialiseConfigMethod(envVars, nodeEngineConfig);
@@ -1832,6 +1832,7 @@ async function extensionsInitialiseEngine(envVars, engineCore) {
1832
1832
  for (const extension of extensions) {
1833
1833
  let initialiseEngineMethod;
1834
1834
  try {
1835
+ engineCore.logInfo(I18n.formatMessage("node.extensionInitialisingEngine", { extension }));
1835
1836
  initialiseEngineMethod =
1836
1837
  await ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngine");
1837
1838
  }
@@ -1855,6 +1856,7 @@ async function extensionsInitialiseEngineServer(envVars, engineCore, engineServe
1855
1856
  for (const extension of extensions) {
1856
1857
  let initialiseEngineServerMethod;
1857
1858
  try {
1859
+ engineCore.logInfo(I18n.formatMessage("node.extensionInitialisingEngineServer", { extension }));
1858
1860
  initialiseEngineServerMethod =
1859
1861
  await ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngineServer");
1860
1862
  }
@@ -1868,14 +1870,16 @@ async function extensionsInitialiseEngineServer(envVars, engineCore, engineServe
1868
1870
  /**
1869
1871
  * Handles the shutdown of the extensions.
1870
1872
  * @param envVars The environment variables for the node.
1873
+ * @param engineCore The engine core instance.
1871
1874
  * @returns Nothing.
1872
1875
  */
1873
- async function shutdownExtensions(envVars) {
1876
+ async function shutdownExtensions(envVars, engineCore) {
1874
1877
  if (Is.stringValue(envVars.extensions)) {
1875
1878
  const extensions = envVars.extensions.split(",");
1876
1879
  for (const extension of extensions) {
1877
1880
  let shutdownMethod;
1878
1881
  try {
1882
+ engineCore.logInfo(I18n.formatMessage("node.extensionShutdown", { extension }));
1879
1883
  shutdownMethod = await ModuleHelper.getModuleMethod(extension, "extensionShutdown");
1880
1884
  }
1881
1885
  catch { }
@@ -1888,7 +1892,6 @@ async function shutdownExtensions(envVars) {
1888
1892
 
1889
1893
  // Copyright 2024 IOTA Stiftung.
1890
1894
  // SPDX-License-Identifier: Apache-2.0.
1891
- /* eslint-disable no-console */
1892
1895
  /**
1893
1896
  * Start the engine server.
1894
1897
  * @param nodeOptions Optional run options for the engine server.
@@ -1919,13 +1922,13 @@ async function start(nodeOptions, nodeEngineConfig, envVars) {
1919
1922
  const server = new EngineServer({ engineCore: engine });
1920
1923
  // Extend the engine.
1921
1924
  if (Is.function(nodeOptions?.extendEngine)) {
1922
- console.info("Extending Engine");
1925
+ engine.logInfo(I18n.formatMessage("node.extendingEngine"));
1923
1926
  await nodeOptions.extendEngine(engine);
1924
1927
  }
1925
1928
  await extensionsInitialiseEngine(envVars, engine);
1926
1929
  // Extend the engine server.
1927
1930
  if (Is.function(nodeOptions?.extendEngineServer)) {
1928
- console.info("Extending Engine Server");
1931
+ engine.logInfo(I18n.formatMessage("node.extendingEngineServer"));
1929
1932
  await nodeOptions?.extendEngineServer(server);
1930
1933
  }
1931
1934
  await extensionsInitialiseEngineServer(envVars, engine, server);
@@ -1940,7 +1943,7 @@ async function start(nodeOptions, nodeEngineConfig, envVars) {
1940
1943
  server,
1941
1944
  shutdown: async () => {
1942
1945
  await server.stop();
1943
- await shutdownExtensions(envVars);
1946
+ await shutdownExtensions(envVars, engine);
1944
1947
  }
1945
1948
  };
1946
1949
  }
@@ -1948,7 +1951,6 @@ async function start(nodeOptions, nodeEngineConfig, envVars) {
1948
1951
 
1949
1952
  // Copyright 2024 IOTA Stiftung.
1950
1953
  // SPDX-License-Identifier: Apache-2.0.
1951
- /* eslint-disable no-console */
1952
1954
  /**
1953
1955
  * Run the TWIN Node server.
1954
1956
  * @param nodeOptions Optional configuration options for running the server.
@@ -1959,42 +1961,48 @@ async function run(nodeOptions) {
1959
1961
  nodeOptions ??= {};
1960
1962
  const serverInfo = {
1961
1963
  name: nodeOptions?.serverName ?? "TWIN Node Server",
1962
- version: nodeOptions?.serverVersion ?? "0.0.2-next.17" // x-release-please-version
1964
+ version: nodeOptions?.serverVersion ?? "0.0.2-next.19" // x-release-please-version
1963
1965
  };
1964
- console.log(`\u001B[4m🌩️ ${serverInfo.name} v${serverInfo.version}\u001B[24m\n`);
1966
+ CLIDisplay.header(serverInfo.name, serverInfo.version, "🌩️ ");
1965
1967
  if (!Is.stringValue(nodeOptions?.executionDirectory)) {
1966
1968
  nodeOptions.executionDirectory = getExecutionDirectory();
1967
1969
  }
1968
- console.info("Execution Directory:", nodeOptions.executionDirectory);
1970
+ CLIDisplay.value("Execution Directory", nodeOptions.executionDirectory);
1969
1971
  nodeOptions.localesDirectory =
1970
1972
  nodeOptions?.localesDirectory ??
1971
1973
  path.resolve(path.join(nodeOptions.executionDirectory, "dist", "locales"));
1972
- console.info("Locales Directory:", nodeOptions.localesDirectory);
1974
+ CLIDisplay.value("Locales Directory", nodeOptions.localesDirectory);
1973
1975
  await initialiseLocales(nodeOptions.localesDirectory);
1974
1976
  if (Is.empty(nodeOptions?.openApiSpecFile)) {
1975
1977
  const specFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "docs", "open-api", "spec.json"));
1976
- console.info("Default OpenAPI Spec File:", specFile);
1978
+ CLIDisplay.value("Default OpenAPI Spec File", specFile);
1977
1979
  if (await fileExists(specFile)) {
1978
1980
  nodeOptions ??= {};
1979
1981
  nodeOptions.openApiSpecFile = specFile;
1980
1982
  }
1981
1983
  }
1984
+ else {
1985
+ CLIDisplay.value("OpenAPI Spec File", nodeOptions.openApiSpecFile);
1986
+ }
1982
1987
  if (Is.empty(nodeOptions?.favIconFile)) {
1983
1988
  const favIconFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "static", "favicon.png"));
1984
- console.info("Default Favicon File:", favIconFile);
1989
+ CLIDisplay.value("Default Favicon File", favIconFile);
1985
1990
  if (await fileExists(favIconFile)) {
1986
1991
  nodeOptions ??= {};
1987
1992
  nodeOptions.favIconFile = favIconFile;
1988
1993
  }
1989
1994
  }
1995
+ else {
1996
+ CLIDisplay.value("Favicon File", nodeOptions.favIconFile);
1997
+ }
1990
1998
  nodeOptions.envPrefix ??= "TWIN_NODE_";
1991
- console.info("Environment Prefix:", nodeOptions.envPrefix);
1999
+ CLIDisplay.value("Environment Variable Prefix", nodeOptions.envPrefix);
1992
2000
  overrideModuleImport(nodeOptions.executionDirectory ?? "");
1993
2001
  const { nodeEngineConfig, nodeEnvVars: envVars } = await buildConfiguration(
1994
2002
  // This is the only location in the code base that should access process.env directly
1995
2003
  // eslint-disable-next-line no-restricted-syntax
1996
2004
  process.env, nodeOptions, serverInfo);
1997
- console.info();
2005
+ CLIDisplay.break();
1998
2006
  const startResult = await start(nodeOptions, nodeEngineConfig, envVars);
1999
2007
  if (!Is.empty(startResult)) {
2000
2008
  for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
@@ -2005,7 +2013,7 @@ async function run(nodeOptions) {
2005
2013
  }
2006
2014
  }
2007
2015
  catch (err) {
2008
- console.error(ErrorHelper.formatErrors(err).join("\n"));
2016
+ CLIDisplay.error(err);
2009
2017
  // eslint-disable-next-line unicorn/no-process-exit
2010
2018
  process.exit(1);
2011
2019
  }
@@ -2022,7 +2030,7 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2022
2030
  let defaultEnvOnly = false;
2023
2031
  if (Is.empty(options?.envFilenames)) {
2024
2032
  const envFile = path.resolve(path.join(options.executionDirectory ?? "", ".env"));
2025
- console.info("Default Environment File:", envFile);
2033
+ CLIDisplay.value("Default Environment File", envFile);
2026
2034
  options ??= {};
2027
2035
  options.envFilenames = [envFile];
2028
2036
  defaultEnvOnly = true;
@@ -2050,18 +2058,18 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2050
2058
  const filePath = envVars[key].slice(6).trim();
2051
2059
  const embeddedFile = path.resolve(path.join(options.executionDirectory ?? "", filePath));
2052
2060
  if (envVars[key].startsWith("@text:")) {
2053
- console.info(`Expanding Environment Variable: ${key} from text file: ${embeddedFile}`);
2061
+ CLIDisplay.value(`Expanding Environment Variable: ${key} from text file`, embeddedFile);
2054
2062
  envVars[key] = await loadTextFile(embeddedFile);
2055
2063
  }
2056
2064
  else if (envVars[key].startsWith("@json:")) {
2057
- console.info(`Expanding Environment Variable: ${key} from JSON file: ${embeddedFile}`);
2065
+ CLIDisplay.value(`Expanding Environment Variable: ${key} from JSON file`, embeddedFile);
2058
2066
  envVars[key] = await loadJsonFile(embeddedFile);
2059
2067
  }
2060
2068
  }
2061
2069
  }
2062
2070
  // Extend the environment variables with any additional custom configuration.
2063
2071
  if (Is.function(options?.extendEnvVars)) {
2064
- console.info("Extending Environment Variables");
2072
+ CLIDisplay.task("Extending Environment Variables");
2065
2073
  await options.extendEnvVars(envVars);
2066
2074
  }
2067
2075
  // Build the engine configuration from the environment variables.
@@ -2070,19 +2078,19 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2070
2078
  // Merge any custom configuration provided in the options.
2071
2079
  if (Is.arrayValue(options?.configFilenames)) {
2072
2080
  for (const configFile of options.configFilenames) {
2073
- console.info("Loading Configuration File:", configFile);
2081
+ CLIDisplay.value("Loading Configuration File", configFile);
2074
2082
  const configFilePath = path.resolve(path.join(options.executionDirectory ?? "", configFile));
2075
2083
  const config = await loadJsonFile(configFilePath);
2076
2084
  Object.assign(engineServerConfig, config);
2077
2085
  }
2078
2086
  }
2079
2087
  if (Is.objectValue(options?.config)) {
2080
- console.info("Merging Custom Configuration");
2088
+ CLIDisplay.task("Merging Custom Configuration");
2081
2089
  Object.assign(engineServerConfig, options.config);
2082
2090
  }
2083
2091
  // Merge any custom configuration provided in the options.
2084
2092
  if (Is.function(options?.extendConfig)) {
2085
- console.info("Extending Configuration");
2093
+ CLIDisplay.task("Extending Configuration");
2086
2094
  await options.extendConfig(envVars, engineServerConfig);
2087
2095
  }
2088
2096
  const nodeEngineConfig = await extensionsConfiguration(envVars, engineServerConfig);
@@ -26,6 +26,7 @@ export declare function extensionsInitialiseEngineServer(envVars: INodeEnvironme
26
26
  /**
27
27
  * Handles the shutdown of the extensions.
28
28
  * @param envVars The environment variables for the node.
29
+ * @param engineCore The engine core instance.
29
30
  * @returns Nothing.
30
31
  */
31
- export declare function shutdownExtensions(envVars: INodeEnvironmentVariables): Promise<void>;
32
+ export declare function shutdownExtensions(envVars: INodeEnvironmentVariables, engineCore: IEngineCore): Promise<void>;
@@ -43,10 +43,6 @@ export interface IEngineServerEnvironmentVariables extends IEngineEnvironmentVar
43
43
  * Additional MIME type processors to include, comma separated.
44
44
  */
45
45
  mimeTypeProcessors?: string;
46
- /**
47
- * Disable Node Identity route processors.
48
- */
49
- disableNodeIdentity?: string;
50
46
  /**
51
47
  * Include the body in the REST logging output, useful for debugging.
52
48
  */
@@ -20,3 +20,8 @@ export type NodeExtensionInitialiseEngineMethod = (engineCore: IEngineCore) => P
20
20
  * @param engineServer The engine server instance.
21
21
  */
22
22
  export type NodeExtensionInitialiseEngineServerMethod = (engineCore: IEngineCore, engineServer: IEngineServer) => Promise<void>;
23
+ /**
24
+ * The type for the shutdown method of an extension module.
25
+ * This is called when the engine is shutting down.
26
+ */
27
+ export type NodeExtensionShutdownMethod = () => Promise<void>;
package/docs/changelog.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @twin.org/node-core - Changelog
2
2
 
3
+ ## [0.0.2-next.19](https://github.com/twinfoundation/node/compare/node-core-v0.0.2-next.18...node-core-v0.0.2-next.19) (2025-10-03)
4
+
5
+
6
+ ### Features
7
+
8
+ * use features instead of disableNodeIdentity ([2e6109b](https://github.com/twinfoundation/node/commit/2e6109bd1de825e0f5396fc120d99ad7b3929852))
9
+
10
+ ## [0.0.2-next.18](https://github.com/twinfoundation/node/compare/node-core-v0.0.2-next.17...node-core-v0.0.2-next.18) (2025-10-03)
11
+
12
+
13
+ ### Features
14
+
15
+ * improve error handling for extensions and logging ([bd84fde](https://github.com/twinfoundation/node/commit/bd84fde67e5a9db6ac66730397992401ca52e88c))
16
+
3
17
  ## [0.0.2-next.17](https://github.com/twinfoundation/node/compare/node-core-v0.0.2-next.16...node-core-v0.0.2-next.17) (2025-10-02)
4
18
 
5
19
 
@@ -1,6 +1,6 @@
1
1
  # Function: shutdownExtensions()
2
2
 
3
- > **shutdownExtensions**(`envVars`): `Promise`\<`void`\>
3
+ > **shutdownExtensions**(`envVars`, `engineCore`): `Promise`\<`void`\>
4
4
 
5
5
  Handles the shutdown of the extensions.
6
6
 
@@ -12,6 +12,12 @@ Handles the shutdown of the extensions.
12
12
 
13
13
  The environment variables for the node.
14
14
 
15
+ ### engineCore
16
+
17
+ `IEngineCore`
18
+
19
+ The engine core instance.
20
+
15
21
  ## Returns
16
22
 
17
23
  `Promise`\<`void`\>
@@ -13,6 +13,7 @@
13
13
  - [NodeExtensionInitialiseMethod](type-aliases/NodeExtensionInitialiseMethod.md)
14
14
  - [NodeExtensionInitialiseEngineMethod](type-aliases/NodeExtensionInitialiseEngineMethod.md)
15
15
  - [NodeExtensionInitialiseEngineServerMethod](type-aliases/NodeExtensionInitialiseEngineServerMethod.md)
16
+ - [NodeExtensionShutdownMethod](type-aliases/NodeExtensionShutdownMethod.md)
16
17
  - [NodeFeatures](type-aliases/NodeFeatures.md)
17
18
 
18
19
  ## Variables
@@ -1740,14 +1740,6 @@ Additional MIME type processors to include, comma separated.
1740
1740
 
1741
1741
  ***
1742
1742
 
1743
- ### disableNodeIdentity?
1744
-
1745
- > `optional` **disableNodeIdentity**: `string`
1746
-
1747
- Disable Node Identity route processors.
1748
-
1749
- ***
1750
-
1751
1743
  ### routeLoggingIncludeBody?
1752
1744
 
1753
1745
  > `optional` **routeLoggingIncludeBody**: `string`
@@ -1776,18 +1776,6 @@ Additional MIME type processors to include, comma separated.
1776
1776
 
1777
1777
  ***
1778
1778
 
1779
- ### disableNodeIdentity?
1780
-
1781
- > `optional` **disableNodeIdentity**: `string`
1782
-
1783
- Disable Node Identity route processors.
1784
-
1785
- #### Inherited from
1786
-
1787
- [`IEngineServerEnvironmentVariables`](IEngineServerEnvironmentVariables.md).[`disableNodeIdentity`](IEngineServerEnvironmentVariables.md#disablenodeidentity)
1788
-
1789
- ***
1790
-
1791
1779
  ### routeLoggingIncludeBody?
1792
1780
 
1793
1781
  > `optional` **routeLoggingIncludeBody**: `string`
@@ -0,0 +1,10 @@
1
+ # Type Alias: NodeExtensionShutdownMethod()
2
+
3
+ > **NodeExtensionShutdownMethod** = () => `Promise`\<`void`\>
4
+
5
+ The type for the shutdown method of an extension module.
6
+ This is called when the engine is shutting down.
7
+
8
+ ## Returns
9
+
10
+ `Promise`\<`void`\>
package/locales/en.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "error": {
3
3
  "node": {
4
- "storageFileRootNotSet": "{storageFileRoot} is not set, the server will not start without it, please set it in the shell or create a .env file"
4
+ "storageFileRootNotSet": "{storageFileRoot} is not set, the server will not start without it, please set it in the shell or create a .env file",
5
+ "extensionLoadingError": "Failed to load extension \"{extension}\""
5
6
  }
6
7
  },
7
8
  "node": {
@@ -31,6 +32,12 @@
31
32
  "existingUserProfile": "User profile already exists \"{identity}\"",
32
33
  "nodeIdentity": "Node identity \"{identity}\"",
33
34
  "nodeAdminUserEmail": "Node Admin User Email \"{email}\"",
34
- "nodeAdminUserPassword": "Node Admin User Password \"{password}\""
35
+ "nodeAdminUserPassword": "Node Admin User Password \"{password}\"",
36
+ "extensionLoading": "Loading Extension",
37
+ "extensionInitialisingEngine": "Initialising engine for extension \"{extension}\"",
38
+ "extensionInitialisingEngineServer": "Initialising engine server for extension \"{extension}\"",
39
+ "extensionShutdown": "Shutdown extension \"{extension}\"",
40
+ "extendingEngine": "Extending Engine",
41
+ "extendingEngineServer": "Extending Engine Server"
35
42
  }
36
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/node-core",
3
- "version": "0.0.2-next.17",
3
+ "version": "0.0.2-next.19",
4
4
  "description": "TWIN Node Core for serving APIs using the specified configuration",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@twin.org/api-auth-entity-storage-service": "next",
18
+ "@twin.org/cli-core": "next",
18
19
  "@twin.org/core": "next",
19
20
  "@twin.org/crypto": "next",
20
21
  "@twin.org/data-space-connector-models": "next",