@twin.org/node-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/README.md +1 -1
- package/dist/es/bootstrap.js +374 -0
- package/dist/es/bootstrap.js.map +1 -0
- package/dist/es/builders/engineEnvBuilder.js +1051 -0
- package/dist/es/builders/engineEnvBuilder.js.map +1 -0
- package/dist/es/builders/engineServerEnvBuilder.js +197 -0
- package/dist/es/builders/engineServerEnvBuilder.js.map +1 -0
- package/dist/es/builders/extensionsBuilder.js +109 -0
- package/dist/es/builders/extensionsBuilder.js.map +1 -0
- package/dist/es/defaults.js +9 -0
- package/dist/es/defaults.js.map +1 -0
- package/dist/es/identity.js +169 -0
- package/dist/es/identity.js.map +1 -0
- package/dist/es/index.js +23 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/ICacheMetadata.js +4 -0
- package/dist/es/models/ICacheMetadata.js.map +1 -0
- package/dist/es/models/IEngineEnvironmentVariables.js +4 -0
- package/dist/es/models/IEngineEnvironmentVariables.js.map +1 -0
- package/dist/es/models/IEngineServerEnvironmentVariables.js +2 -0
- package/dist/es/models/IEngineServerEnvironmentVariables.js.map +1 -0
- package/dist/es/models/IModuleProtocol.js +2 -0
- package/dist/es/models/IModuleProtocol.js.map +1 -0
- package/dist/es/models/INodeEngineConfig.js +2 -0
- package/dist/es/models/INodeEngineConfig.js.map +1 -0
- package/dist/es/models/INodeEngineState.js +2 -0
- package/dist/es/models/INodeEngineState.js.map +1 -0
- package/dist/es/models/INodeEnvironmentVariables.js +2 -0
- package/dist/es/models/INodeEnvironmentVariables.js.map +1 -0
- package/dist/es/models/INodeOptions.js +2 -0
- package/dist/es/models/INodeOptions.js.map +1 -0
- package/dist/es/models/IProtocolHandlerResult.js +4 -0
- package/dist/es/models/IProtocolHandlerResult.js.map +1 -0
- package/dist/es/models/moduleProtocol.js +29 -0
- package/dist/es/models/moduleProtocol.js.map +1 -0
- package/dist/es/models/nodeExtensionMethods.js +2 -0
- package/dist/es/models/nodeExtensionMethods.js.map +1 -0
- package/dist/es/models/nodeFeatures.js +21 -0
- package/dist/es/models/nodeFeatures.js.map +1 -0
- package/dist/es/node.js +265 -0
- package/dist/es/node.js.map +1 -0
- package/dist/es/server.js +78 -0
- package/dist/es/server.js.map +1 -0
- package/dist/es/utils.js +418 -0
- package/dist/es/utils.js.map +1 -0
- package/dist/types/bootstrap.d.ts +27 -10
- package/dist/types/builders/engineEnvBuilder.d.ts +3 -2
- package/dist/types/builders/engineServerEnvBuilder.d.ts +4 -3
- package/dist/types/builders/extensionsBuilder.d.ts +32 -0
- package/dist/types/defaults.d.ts +6 -0
- package/dist/types/identity.d.ts +14 -0
- package/dist/types/index.d.ts +20 -11
- package/dist/types/models/ICacheMetadata.d.ts +17 -0
- package/dist/types/models/IEngineEnvironmentVariables.d.ts +132 -24
- package/dist/types/models/IEngineServerEnvironmentVariables.d.ts +12 -3
- package/dist/types/models/IModuleProtocol.d.ts +18 -0
- package/dist/types/models/INodeEngineConfig.d.ts +6 -0
- package/dist/types/models/INodeEngineState.d.ts +22 -0
- package/dist/types/models/INodeEnvironmentVariables.d.ts +50 -10
- package/dist/types/models/INodeOptions.d.ts +14 -3
- package/dist/types/models/IProtocolHandlerResult.d.ts +13 -0
- package/dist/types/models/moduleProtocol.d.ts +29 -0
- package/dist/types/models/nodeExtensionMethods.d.ts +27 -0
- package/dist/types/models/nodeFeatures.d.ts +5 -5
- package/dist/types/node.d.ts +17 -7
- package/dist/types/server.d.ts +9 -6
- package/dist/types/utils.d.ts +87 -2
- package/docs/changelog.md +185 -0
- package/docs/detailed-guide.md +129 -0
- package/docs/reference/functions/bootstrap.md +1 -1
- package/docs/reference/functions/bootstrapAuth.md +1 -1
- package/docs/reference/functions/bootstrapBlobEncryption.md +1 -1
- package/docs/reference/functions/bootstrapContextIdHandlers.md +35 -0
- package/docs/reference/functions/bootstrapImmutableProofMethod.md +1 -1
- package/docs/reference/functions/{bootstrapNodeUser.md → bootstrapNodeAdminUser.md} +3 -3
- package/docs/reference/functions/{bootstrapNodeIdentity.md → bootstrapNodeId.md} +3 -3
- package/docs/reference/functions/bootstrapSynchronisedStorage.md +1 -1
- package/docs/reference/functions/bootstrapTenantId.md +35 -0
- package/docs/reference/functions/buildConfiguration.md +2 -2
- package/docs/reference/functions/buildEngineConfiguration.md +8 -2
- package/docs/reference/functions/buildEngineServerConfiguration.md +9 -3
- package/docs/reference/functions/createModuleImportUrl.md +21 -0
- package/docs/reference/functions/directoryExists.md +19 -0
- package/docs/reference/functions/extensionsConfiguration.md +25 -0
- package/docs/reference/functions/extensionsInitialiseEngine.md +25 -0
- package/docs/reference/functions/extensionsInitialiseEngineServer.md +31 -0
- package/docs/reference/functions/getExtensionsCacheDir.md +31 -0
- package/docs/reference/functions/getFiles.md +19 -0
- package/docs/reference/functions/getSubFolders.md +19 -0
- package/docs/reference/functions/handleHttpsProtocol.md +49 -0
- package/docs/reference/functions/handleNpmProtocol.md +31 -0
- package/docs/reference/functions/hashUrl.md +19 -0
- package/docs/reference/functions/isCacheExpired.md +31 -0
- package/docs/reference/functions/overrideModuleImport.md +8 -2
- package/docs/reference/functions/parseModuleProtocol.md +19 -0
- package/docs/reference/functions/resolvePackageEntryPoint.md +32 -0
- package/docs/reference/functions/run.md +3 -3
- package/docs/reference/functions/shutdownExtensions.md +25 -0
- package/docs/reference/functions/start.md +11 -5
- package/docs/reference/index.md +36 -2
- package/docs/reference/interfaces/ICacheMetadata.md +27 -0
- package/docs/reference/interfaces/IEngineEnvironmentVariables.md +255 -37
- package/docs/reference/interfaces/IEngineServerEnvironmentVariables.md +1696 -5
- package/docs/reference/interfaces/IModuleProtocol.md +27 -0
- package/docs/reference/interfaces/INodeEngineConfig.md +7 -0
- package/docs/reference/interfaces/INodeEngineState.md +39 -0
- package/docs/reference/interfaces/INodeEnvironmentVariables.md +609 -165
- package/docs/reference/interfaces/INodeOptions.md +27 -3
- package/docs/reference/interfaces/IProtocolHandlerResult.md +19 -0
- package/docs/reference/type-aliases/ModuleProtocol.md +5 -0
- package/docs/reference/type-aliases/NodeExtensionInitialiseEngineMethod.md +18 -0
- package/docs/reference/type-aliases/NodeExtensionInitialiseEngineServerMethod.md +24 -0
- package/docs/reference/type-aliases/NodeExtensionInitialiseMethod.md +23 -0
- package/docs/reference/type-aliases/NodeExtensionShutdownMethod.md +10 -0
- package/docs/reference/variables/ATTESTATION_VERIFICATION_METHOD_ID.md +3 -0
- package/docs/reference/variables/AUTH_SIGNING_KEY_ID.md +3 -0
- package/docs/reference/variables/BLOB_STORAGE_ENCRYPTION_KEY_ID.md +3 -0
- package/docs/reference/variables/IMMUTABLE_PROOF_VERIFICATION_METHOD_ID.md +3 -0
- package/docs/reference/variables/ModuleProtocol.md +37 -0
- package/docs/reference/variables/NodeFeatures.md +7 -7
- package/docs/reference/variables/SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID.md +3 -0
- package/docs/reference/variables/VC_AUTHENTICATION_VERIFICATION_METHOD_ID.md +3 -0
- package/locales/en.json +27 -9
- package/package.json +27 -9
- package/dist/cjs/index.cjs +0 -1910
- package/dist/esm/index.mjs +0 -1870
package/dist/es/node.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { CLIDisplay } from "@twin.org/cli-core";
|
|
6
|
+
import { Coerce, EnvHelper, GeneralError, Is } from "@twin.org/core";
|
|
7
|
+
import { ModuleHelper } from "@twin.org/modules";
|
|
8
|
+
import * as dotenv from "dotenv";
|
|
9
|
+
import { buildEngineConfiguration } from "./builders/engineEnvBuilder.js";
|
|
10
|
+
import { buildEngineServerConfiguration } from "./builders/engineServerEnvBuilder.js";
|
|
11
|
+
import { extensionsConfiguration } from "./builders/extensionsBuilder.js";
|
|
12
|
+
import { ModuleProtocol } from "./models/moduleProtocol.js";
|
|
13
|
+
import { start } from "./server.js";
|
|
14
|
+
import { createModuleImportUrl, fileExists, getExecutionDirectory, getExtensionsCacheDir, handleHttpsProtocol, handleNpmProtocol, initialiseLocales, loadJsonFile, loadTextFile, parseModuleProtocol, resolvePackageEntryPoint } from "./utils.js";
|
|
15
|
+
const moduleCache = {};
|
|
16
|
+
/**
|
|
17
|
+
* Run the TWIN Node server.
|
|
18
|
+
* @param nodeOptions Optional configuration options for running the server.
|
|
19
|
+
* @returns A promise that resolves when the server is started containing a shutdown method.
|
|
20
|
+
*/
|
|
21
|
+
export async function run(nodeOptions) {
|
|
22
|
+
try {
|
|
23
|
+
nodeOptions ??= {};
|
|
24
|
+
const serverInfo = {
|
|
25
|
+
name: nodeOptions?.serverName ?? "TWIN Node Server",
|
|
26
|
+
version: nodeOptions?.serverVersion ?? "0.0.3-next.0" // x-release-please-version
|
|
27
|
+
};
|
|
28
|
+
CLIDisplay.header(serverInfo.name, serverInfo.version, "🌩️ ");
|
|
29
|
+
if (!Is.stringValue(nodeOptions?.executionDirectory)) {
|
|
30
|
+
nodeOptions.executionDirectory = getExecutionDirectory();
|
|
31
|
+
}
|
|
32
|
+
CLIDisplay.value("Execution Directory", nodeOptions.executionDirectory);
|
|
33
|
+
nodeOptions.localesDirectory =
|
|
34
|
+
nodeOptions?.localesDirectory ??
|
|
35
|
+
path.resolve(path.join(nodeOptions.executionDirectory, "dist", "locales"));
|
|
36
|
+
CLIDisplay.value("Locales Directory", nodeOptions.localesDirectory);
|
|
37
|
+
await initialiseLocales(nodeOptions.localesDirectory);
|
|
38
|
+
if (Is.empty(nodeOptions?.openApiSpecFile)) {
|
|
39
|
+
const specFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "docs", "open-api", "spec.json"));
|
|
40
|
+
CLIDisplay.value("Default OpenAPI Spec File", specFile);
|
|
41
|
+
if (await fileExists(specFile)) {
|
|
42
|
+
nodeOptions ??= {};
|
|
43
|
+
nodeOptions.openApiSpecFile = specFile;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
CLIDisplay.value("OpenAPI Spec File", nodeOptions.openApiSpecFile);
|
|
48
|
+
}
|
|
49
|
+
if (Is.empty(nodeOptions?.favIconFile)) {
|
|
50
|
+
const favIconFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "static", "favicon.png"));
|
|
51
|
+
CLIDisplay.value("Default Favicon File", favIconFile);
|
|
52
|
+
if (await fileExists(favIconFile)) {
|
|
53
|
+
nodeOptions ??= {};
|
|
54
|
+
nodeOptions.favIconFile = favIconFile;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
CLIDisplay.value("Favicon File", nodeOptions.favIconFile);
|
|
59
|
+
}
|
|
60
|
+
nodeOptions.envPrefix ??= "TWIN_";
|
|
61
|
+
CLIDisplay.value("Environment Variable Prefix", nodeOptions.envPrefix);
|
|
62
|
+
overrideModuleImport(nodeOptions.executionDirectory ?? "");
|
|
63
|
+
// This is the only location in the code base that should access process.env directly
|
|
64
|
+
// So we can safely disable the linting rule here.
|
|
65
|
+
let finalEnvVars =
|
|
66
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
67
|
+
process.env;
|
|
68
|
+
if (Is.objectValue(nodeOptions?.envVars)) {
|
|
69
|
+
finalEnvVars = {
|
|
70
|
+
...finalEnvVars,
|
|
71
|
+
...nodeOptions.envVars
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const { nodeEngineConfig, nodeEnvVars: envVars, contextIdKeys } = await buildConfiguration(finalEnvVars, nodeOptions, serverInfo);
|
|
75
|
+
CLIDisplay.break();
|
|
76
|
+
const startResult = await start(nodeOptions, nodeEngineConfig, envVars, contextIdKeys);
|
|
77
|
+
if (!Is.empty(startResult)) {
|
|
78
|
+
for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
|
|
79
|
+
process.on(signal, async () => {
|
|
80
|
+
CLIDisplay.value("Terminate Signal", signal);
|
|
81
|
+
await startResult.shutdown();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return startResult;
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (nodeOptions?.disableProcessExitOnFailure ?? false) {
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
CLIDisplay.error(err);
|
|
92
|
+
// eslint-disable-next-line unicorn/no-process-exit
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Build the configuration for the TWIN Node server.
|
|
98
|
+
* @param processEnv The environment variables from the process.
|
|
99
|
+
* @param options The options for running the server.
|
|
100
|
+
* @param serverInfo The server information.
|
|
101
|
+
* @returns A promise that resolves to the engine server configuration, environment prefix, environment variables,
|
|
102
|
+
* and options.
|
|
103
|
+
*/
|
|
104
|
+
export async function buildConfiguration(processEnv, options, serverInfo) {
|
|
105
|
+
const contextIdKeys = [];
|
|
106
|
+
let defaultEnvOnly = false;
|
|
107
|
+
if (Is.empty(options?.envFilenames)) {
|
|
108
|
+
const envFile = path.resolve(path.join(options.executionDirectory ?? "", ".env"));
|
|
109
|
+
CLIDisplay.value("Default Environment File", envFile);
|
|
110
|
+
options ??= {};
|
|
111
|
+
options.envFilenames = [envFile];
|
|
112
|
+
defaultEnvOnly = true;
|
|
113
|
+
}
|
|
114
|
+
if (Is.arrayValue(options?.envFilenames)) {
|
|
115
|
+
const output = dotenv.config({
|
|
116
|
+
path: options?.envFilenames,
|
|
117
|
+
quiet: true
|
|
118
|
+
});
|
|
119
|
+
// We don't want to throw an error if the default environment file is not found.
|
|
120
|
+
// Only if we have custom environment files.
|
|
121
|
+
if (!defaultEnvOnly && output.error) {
|
|
122
|
+
throw output.error;
|
|
123
|
+
}
|
|
124
|
+
if (Is.objectValue(output.parsed)) {
|
|
125
|
+
Object.assign(processEnv, output.parsed);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const envVars = EnvHelper.envToJson(processEnv, options.envPrefix ?? "");
|
|
129
|
+
// Expand any environment variables that use the @file: syntax
|
|
130
|
+
const keys = Object.keys(envVars);
|
|
131
|
+
for (const key of keys) {
|
|
132
|
+
if (Is.stringValue(envVars[key]) &&
|
|
133
|
+
(envVars[key].startsWith("@text:") || envVars[key].startsWith("@json:"))) {
|
|
134
|
+
const filePath = envVars[key].slice(6).trim();
|
|
135
|
+
const embeddedFile = path.resolve(path.join(options.executionDirectory ?? "", filePath));
|
|
136
|
+
if (envVars[key].startsWith("@text:")) {
|
|
137
|
+
CLIDisplay.value(`Expanding Environment Variable: ${key} from text file`, embeddedFile);
|
|
138
|
+
envVars[key] = await loadTextFile(embeddedFile);
|
|
139
|
+
}
|
|
140
|
+
else if (envVars[key].startsWith("@json:")) {
|
|
141
|
+
CLIDisplay.value(`Expanding Environment Variable: ${key} from JSON file`, embeddedFile);
|
|
142
|
+
envVars[key] = await loadJsonFile(embeddedFile);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Extend the environment variables with any additional custom configuration.
|
|
147
|
+
if (Is.function(options?.extendEnvVars)) {
|
|
148
|
+
CLIDisplay.task("Extending Environment Variables");
|
|
149
|
+
await options.extendEnvVars(envVars);
|
|
150
|
+
}
|
|
151
|
+
// Build the engine configuration from the environment variables.
|
|
152
|
+
const coreConfig = await buildEngineConfiguration(envVars, contextIdKeys);
|
|
153
|
+
const engineServerConfig = await buildEngineServerConfiguration(envVars, contextIdKeys, coreConfig, serverInfo, options?.openApiSpecFile, options?.favIconFile);
|
|
154
|
+
// Merge any custom configuration provided in the options.
|
|
155
|
+
if (Is.arrayValue(options?.configFilenames)) {
|
|
156
|
+
for (const configFile of options.configFilenames) {
|
|
157
|
+
CLIDisplay.value("Loading Configuration File", configFile);
|
|
158
|
+
const configFilePath = path.resolve(path.join(options.executionDirectory ?? "", configFile));
|
|
159
|
+
const config = await loadJsonFile(configFilePath);
|
|
160
|
+
Object.assign(engineServerConfig, config);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (Is.objectValue(options?.config)) {
|
|
164
|
+
CLIDisplay.task("Merging Custom Configuration");
|
|
165
|
+
Object.assign(engineServerConfig, options.config);
|
|
166
|
+
}
|
|
167
|
+
// Merge any custom configuration provided in the options.
|
|
168
|
+
if (Is.function(options?.extendConfig)) {
|
|
169
|
+
CLIDisplay.task("Extending Configuration");
|
|
170
|
+
await options.extendConfig(envVars, engineServerConfig);
|
|
171
|
+
}
|
|
172
|
+
const nodeEngineConfig = await extensionsConfiguration(envVars, engineServerConfig);
|
|
173
|
+
return { nodeEngineConfig, nodeEnvVars: envVars, contextIdKeys };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Override module imports to support protocol-based loading (npm:, https:) and local files.
|
|
177
|
+
* @param executionDirectory The execution directory for resolving local module paths.
|
|
178
|
+
* @param envVars The environment variables containing extension configuration (optional, uses defaults if not provided).
|
|
179
|
+
*/
|
|
180
|
+
export function overrideModuleImport(executionDirectory, envVars) {
|
|
181
|
+
const maxSizeMb = Coerce.number(envVars?.extensionsMaxSizeMb) ?? 10;
|
|
182
|
+
const cacheDirectory = envVars?.extensionsCacheDirectory;
|
|
183
|
+
ModuleHelper.overrideImport(async (moduleName) => {
|
|
184
|
+
if (moduleCache[moduleName]) {
|
|
185
|
+
return {
|
|
186
|
+
module: moduleCache[moduleName],
|
|
187
|
+
useDefault: false
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const parsed = parseModuleProtocol(moduleName);
|
|
191
|
+
let resolvedPath;
|
|
192
|
+
switch (parsed.protocol) {
|
|
193
|
+
case ModuleProtocol.Npm: {
|
|
194
|
+
const result = await handleNpmProtocol(parsed.identifier, executionDirectory, cacheDirectory);
|
|
195
|
+
resolvedPath = result.resolvedPath;
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case ModuleProtocol.Https: {
|
|
199
|
+
const result = await handleHttpsProtocol(parsed.identifier, executionDirectory, maxSizeMb, cacheDirectory, envVars?.extensionsCacheTtlHours, envVars?.extensionsForceRefresh);
|
|
200
|
+
resolvedPath = result.resolvedPath;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case ModuleProtocol.Http: {
|
|
204
|
+
throw new GeneralError("node", "insecureProtocol", { protocol: ModuleProtocol.Http });
|
|
205
|
+
}
|
|
206
|
+
case ModuleProtocol.Local: {
|
|
207
|
+
let localFilename = path.resolve(moduleName);
|
|
208
|
+
let exists = await fileExists(localFilename);
|
|
209
|
+
if (!exists) {
|
|
210
|
+
localFilename = path.resolve(executionDirectory, moduleName);
|
|
211
|
+
exists = await fileExists(localFilename);
|
|
212
|
+
}
|
|
213
|
+
if (exists) {
|
|
214
|
+
resolvedPath = localFilename;
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case ModuleProtocol.Default: {
|
|
219
|
+
try {
|
|
220
|
+
const npmRoot = execSync("npm root").toString().trim().replace(/\\/g, "/");
|
|
221
|
+
const packagePath = path.resolve(npmRoot, moduleName);
|
|
222
|
+
const mainFile = await resolvePackageEntryPoint(packagePath, moduleName);
|
|
223
|
+
const modulePath = path.resolve(packagePath, mainFile);
|
|
224
|
+
const exists = await fileExists(modulePath);
|
|
225
|
+
if (exists) {
|
|
226
|
+
resolvedPath = modulePath;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Continue to fallback resolution
|
|
232
|
+
}
|
|
233
|
+
// Fallback: resolve from npm protocol cache directory (installed via handleNpmProtocol)
|
|
234
|
+
try {
|
|
235
|
+
const cacheNpmRoot = path.resolve(getExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory), "node_modules");
|
|
236
|
+
const packagePath = path.resolve(cacheNpmRoot, moduleName);
|
|
237
|
+
const mainFile = await resolvePackageEntryPoint(packagePath, moduleName);
|
|
238
|
+
const modulePath = path.resolve(packagePath, mainFile);
|
|
239
|
+
const exists = await fileExists(modulePath);
|
|
240
|
+
if (exists) {
|
|
241
|
+
resolvedPath = modulePath;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// No cached resolution either; fall through
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Common module loading and caching logic
|
|
251
|
+
if (resolvedPath) {
|
|
252
|
+
const module = await import(createModuleImportUrl(resolvedPath));
|
|
253
|
+
moduleCache[moduleName] = module;
|
|
254
|
+
return {
|
|
255
|
+
module,
|
|
256
|
+
useDefault: false
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
module: undefined,
|
|
261
|
+
useDefault: true
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/node.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { execSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type { IServerInfo } from \"@twin.org/api-models\";\nimport { CLIDisplay } from \"@twin.org/cli-core\";\nimport { Coerce, EnvHelper, GeneralError, Is } from \"@twin.org/core\";\nimport type { Engine } from \"@twin.org/engine\";\nimport type { EngineServer } from \"@twin.org/engine-server\";\nimport type { IEngineServerConfig } from \"@twin.org/engine-server-types\";\nimport { ModuleHelper } from \"@twin.org/modules\";\nimport * as dotenv from \"dotenv\";\nimport { buildEngineConfiguration } from \"./builders/engineEnvBuilder.js\";\nimport { buildEngineServerConfiguration } from \"./builders/engineServerEnvBuilder.js\";\nimport { extensionsConfiguration } from \"./builders/extensionsBuilder.js\";\nimport type { INodeEngineConfig } from \"./models/INodeEngineConfig.js\";\nimport type { INodeEngineState } from \"./models/INodeEngineState.js\";\nimport type { INodeEnvironmentVariables } from \"./models/INodeEnvironmentVariables.js\";\nimport type { INodeOptions } from \"./models/INodeOptions.js\";\nimport { ModuleProtocol } from \"./models/moduleProtocol.js\";\nimport { start } from \"./server.js\";\nimport {\n\tcreateModuleImportUrl,\n\tfileExists,\n\tgetExecutionDirectory,\n\tgetExtensionsCacheDir,\n\thandleHttpsProtocol,\n\thandleNpmProtocol,\n\tinitialiseLocales,\n\tloadJsonFile,\n\tloadTextFile,\n\tparseModuleProtocol,\n\tresolvePackageEntryPoint\n} from \"./utils.js\";\n\nconst moduleCache: { [id: string]: unknown } = {};\n\n/**\n * Run the TWIN Node server.\n * @param nodeOptions Optional configuration options for running the server.\n * @returns A promise that resolves when the server is started containing a shutdown method.\n */\nexport async function run(nodeOptions?: INodeOptions): Promise<\n\t| {\n\t\t\tengine: Engine<IEngineServerConfig, INodeEngineState>;\n\t\t\tserver: EngineServer;\n\t\t\tshutdown: () => Promise<void>;\n\t }\n\t| undefined\n> {\n\ttry {\n\t\tnodeOptions ??= {};\n\n\t\tconst serverInfo: IServerInfo = {\n\t\t\tname: nodeOptions?.serverName ?? \"TWIN Node Server\",\n\t\t\tversion: nodeOptions?.serverVersion ?? \"0.0.3-next.0\" // x-release-please-version\n\t\t};\n\n\t\tCLIDisplay.header(serverInfo.name, serverInfo.version, \"🌩️ \");\n\n\t\tif (!Is.stringValue(nodeOptions?.executionDirectory)) {\n\t\t\tnodeOptions.executionDirectory = getExecutionDirectory();\n\t\t}\n\t\tCLIDisplay.value(\"Execution Directory\", nodeOptions.executionDirectory);\n\n\t\tnodeOptions.localesDirectory =\n\t\t\tnodeOptions?.localesDirectory ??\n\t\t\tpath.resolve(path.join(nodeOptions.executionDirectory, \"dist\", \"locales\"));\n\t\tCLIDisplay.value(\"Locales Directory\", nodeOptions.localesDirectory);\n\n\t\tawait initialiseLocales(nodeOptions.localesDirectory);\n\n\t\tif (Is.empty(nodeOptions?.openApiSpecFile)) {\n\t\t\tconst specFile = path.resolve(\n\t\t\t\tpath.join(nodeOptions.executionDirectory ?? \"\", \"docs\", \"open-api\", \"spec.json\")\n\t\t\t);\n\t\t\tCLIDisplay.value(\"Default OpenAPI Spec File\", specFile);\n\t\t\tif (await fileExists(specFile)) {\n\t\t\t\tnodeOptions ??= {};\n\t\t\t\tnodeOptions.openApiSpecFile = specFile;\n\t\t\t}\n\t\t} else {\n\t\t\tCLIDisplay.value(\"OpenAPI Spec File\", nodeOptions.openApiSpecFile);\n\t\t}\n\n\t\tif (Is.empty(nodeOptions?.favIconFile)) {\n\t\t\tconst favIconFile = path.resolve(\n\t\t\t\tpath.join(nodeOptions.executionDirectory ?? \"\", \"static\", \"favicon.png\")\n\t\t\t);\n\t\t\tCLIDisplay.value(\"Default Favicon File\", favIconFile);\n\t\t\tif (await fileExists(favIconFile)) {\n\t\t\t\tnodeOptions ??= {};\n\t\t\t\tnodeOptions.favIconFile = favIconFile;\n\t\t\t}\n\t\t} else {\n\t\t\tCLIDisplay.value(\"Favicon File\", nodeOptions.favIconFile);\n\t\t}\n\n\t\tnodeOptions.envPrefix ??= \"TWIN_\";\n\t\tCLIDisplay.value(\"Environment Variable Prefix\", nodeOptions.envPrefix);\n\n\t\toverrideModuleImport(nodeOptions.executionDirectory ?? \"\");\n\n\t\t// This is the only location in the code base that should access process.env directly\n\t\t// So we can safely disable the linting rule here.\n\t\tlet finalEnvVars =\n\t\t\t// eslint-disable-next-line no-restricted-syntax\n\t\t\tprocess.env as {\n\t\t\t\t[id: string]: string;\n\t\t\t};\n\n\t\tif (Is.objectValue(nodeOptions?.envVars)) {\n\t\t\tfinalEnvVars = {\n\t\t\t\t...finalEnvVars,\n\t\t\t\t...nodeOptions.envVars\n\t\t\t};\n\t\t}\n\n\t\tconst {\n\t\t\tnodeEngineConfig,\n\t\t\tnodeEnvVars: envVars,\n\t\t\tcontextIdKeys\n\t\t} = await buildConfiguration(finalEnvVars, nodeOptions, serverInfo);\n\n\t\tCLIDisplay.break();\n\t\tconst startResult = await start(nodeOptions, nodeEngineConfig, envVars, contextIdKeys);\n\n\t\tif (!Is.empty(startResult)) {\n\t\t\tfor (const signal of [\"SIGHUP\", \"SIGINT\", \"SIGTERM\"]) {\n\t\t\t\tprocess.on(signal, async () => {\n\t\t\t\t\tCLIDisplay.value(\"Terminate Signal\", signal);\n\t\t\t\t\tawait startResult.shutdown();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn startResult;\n\t} catch (err) {\n\t\tif (nodeOptions?.disableProcessExitOnFailure ?? false) {\n\t\t\tthrow err;\n\t\t}\n\t\tCLIDisplay.error(err);\n\t\t// eslint-disable-next-line unicorn/no-process-exit\n\t\tprocess.exit(1);\n\t}\n}\n\n/**\n * Build the configuration for the TWIN Node server.\n * @param processEnv The environment variables from the process.\n * @param options The options for running the server.\n * @param serverInfo The server information.\n * @returns A promise that resolves to the engine server configuration, environment prefix, environment variables,\n * and options.\n */\nexport async function buildConfiguration(\n\tprocessEnv: {\n\t\t[id: string]: string;\n\t},\n\toptions: INodeOptions,\n\tserverInfo: IServerInfo\n): Promise<{\n\tnodeEnvVars: INodeEnvironmentVariables & { [id: string]: string | unknown };\n\tnodeEngineConfig: INodeEngineConfig;\n\tcontextIdKeys: string[];\n}> {\n\tconst contextIdKeys: string[] = [];\n\n\tlet defaultEnvOnly = false;\n\tif (Is.empty(options?.envFilenames)) {\n\t\tconst envFile = path.resolve(path.join(options.executionDirectory ?? \"\", \".env\"));\n\t\tCLIDisplay.value(\"Default Environment File\", envFile);\n\t\toptions ??= {};\n\t\toptions.envFilenames = [envFile];\n\t\tdefaultEnvOnly = true;\n\t}\n\n\tif (Is.arrayValue(options?.envFilenames)) {\n\t\tconst output = dotenv.config({\n\t\t\tpath: options?.envFilenames,\n\t\t\tquiet: true\n\t\t});\n\n\t\t// We don't want to throw an error if the default environment file is not found.\n\t\t// Only if we have custom environment files.\n\t\tif (!defaultEnvOnly && output.error) {\n\t\t\tthrow output.error;\n\t\t}\n\n\t\tif (Is.objectValue(output.parsed)) {\n\t\t\tObject.assign(processEnv, output.parsed);\n\t\t}\n\t}\n\n\tconst envVars = EnvHelper.envToJson<{ [id: string]: string | unknown }>(\n\t\tprocessEnv,\n\t\toptions.envPrefix ?? \"\"\n\t);\n\n\t// Expand any environment variables that use the @file: syntax\n\tconst keys = Object.keys(envVars);\n\tfor (const key of keys) {\n\t\tif (\n\t\t\tIs.stringValue(envVars[key]) &&\n\t\t\t(envVars[key].startsWith(\"@text:\") || envVars[key].startsWith(\"@json:\"))\n\t\t) {\n\t\t\tconst filePath = envVars[key].slice(6).trim();\n\t\t\tconst embeddedFile = path.resolve(path.join(options.executionDirectory ?? \"\", filePath));\n\n\t\t\tif (envVars[key].startsWith(\"@text:\")) {\n\t\t\t\tCLIDisplay.value(`Expanding Environment Variable: ${key} from text file`, embeddedFile);\n\t\t\t\tenvVars[key] = await loadTextFile(embeddedFile);\n\t\t\t} else if (envVars[key].startsWith(\"@json:\")) {\n\t\t\t\tCLIDisplay.value(`Expanding Environment Variable: ${key} from JSON file`, embeddedFile);\n\t\t\t\tenvVars[key] = await loadJsonFile(embeddedFile);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Extend the environment variables with any additional custom configuration.\n\tif (Is.function(options?.extendEnvVars)) {\n\t\tCLIDisplay.task(\"Extending Environment Variables\");\n\t\tawait options.extendEnvVars(envVars);\n\t}\n\n\t// Build the engine configuration from the environment variables.\n\tconst coreConfig = await buildEngineConfiguration(envVars, contextIdKeys);\n\tconst engineServerConfig = await buildEngineServerConfiguration(\n\t\tenvVars,\n\t\tcontextIdKeys,\n\t\tcoreConfig,\n\t\tserverInfo,\n\t\toptions?.openApiSpecFile,\n\t\toptions?.favIconFile\n\t);\n\n\t// Merge any custom configuration provided in the options.\n\tif (Is.arrayValue(options?.configFilenames)) {\n\t\tfor (const configFile of options.configFilenames) {\n\t\t\tCLIDisplay.value(\"Loading Configuration File\", configFile);\n\t\t\tconst configFilePath = path.resolve(path.join(options.executionDirectory ?? \"\", configFile));\n\t\t\tconst config = await loadJsonFile(configFilePath);\n\t\t\tObject.assign(engineServerConfig, config);\n\t\t}\n\t}\n\n\tif (Is.objectValue(options?.config)) {\n\t\tCLIDisplay.task(\"Merging Custom Configuration\");\n\t\tObject.assign(engineServerConfig, options.config);\n\t}\n\n\t// Merge any custom configuration provided in the options.\n\tif (Is.function(options?.extendConfig)) {\n\t\tCLIDisplay.task(\"Extending Configuration\");\n\t\tawait options.extendConfig(envVars, engineServerConfig);\n\t}\n\n\tconst nodeEngineConfig = await extensionsConfiguration(envVars, engineServerConfig);\n\n\treturn { nodeEngineConfig, nodeEnvVars: envVars, contextIdKeys };\n}\n\n/**\n * Override module imports to support protocol-based loading (npm:, https:) and local files.\n * @param executionDirectory The execution directory for resolving local module paths.\n * @param envVars The environment variables containing extension configuration (optional, uses defaults if not provided).\n */\nexport function overrideModuleImport(\n\texecutionDirectory: string,\n\tenvVars?: INodeEnvironmentVariables\n): void {\n\tconst maxSizeMb = Coerce.number(envVars?.extensionsMaxSizeMb) ?? 10;\n\tconst cacheDirectory = envVars?.extensionsCacheDirectory;\n\n\tModuleHelper.overrideImport(async moduleName => {\n\t\tif (moduleCache[moduleName]) {\n\t\t\treturn {\n\t\t\t\tmodule: moduleCache[moduleName],\n\t\t\t\tuseDefault: false\n\t\t\t};\n\t\t}\n\n\t\tconst parsed = parseModuleProtocol(moduleName);\n\t\tlet resolvedPath: string | undefined;\n\n\t\tswitch (parsed.protocol) {\n\t\t\tcase ModuleProtocol.Npm: {\n\t\t\t\tconst result = await handleNpmProtocol(\n\t\t\t\t\tparsed.identifier,\n\t\t\t\t\texecutionDirectory,\n\t\t\t\t\tcacheDirectory\n\t\t\t\t);\n\t\t\t\tresolvedPath = result.resolvedPath;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase ModuleProtocol.Https: {\n\t\t\t\tconst result = await handleHttpsProtocol(\n\t\t\t\t\tparsed.identifier,\n\t\t\t\t\texecutionDirectory,\n\t\t\t\t\tmaxSizeMb,\n\t\t\t\t\tcacheDirectory,\n\t\t\t\t\tenvVars?.extensionsCacheTtlHours,\n\t\t\t\t\tenvVars?.extensionsForceRefresh\n\t\t\t\t);\n\t\t\t\tresolvedPath = result.resolvedPath;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase ModuleProtocol.Http: {\n\t\t\t\tthrow new GeneralError(\"node\", \"insecureProtocol\", { protocol: ModuleProtocol.Http });\n\t\t\t}\n\n\t\t\tcase ModuleProtocol.Local: {\n\t\t\t\tlet localFilename = path.resolve(moduleName);\n\n\t\t\t\tlet exists = await fileExists(localFilename);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tlocalFilename = path.resolve(executionDirectory, moduleName);\n\t\t\t\t\texists = await fileExists(localFilename);\n\t\t\t\t}\n\n\t\t\t\tif (exists) {\n\t\t\t\t\tresolvedPath = localFilename;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase ModuleProtocol.Default: {\n\t\t\t\ttry {\n\t\t\t\t\tconst npmRoot = execSync(\"npm root\").toString().trim().replace(/\\\\/g, \"/\");\n\t\t\t\t\tconst packagePath = path.resolve(npmRoot, moduleName);\n\t\t\t\t\tconst mainFile = await resolvePackageEntryPoint(packagePath, moduleName);\n\t\t\t\t\tconst modulePath = path.resolve(packagePath, mainFile);\n\t\t\t\t\tconst exists = await fileExists(modulePath);\n\t\t\t\t\tif (exists) {\n\t\t\t\t\t\tresolvedPath = modulePath;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Continue to fallback resolution\n\t\t\t\t}\n\n\t\t\t\t// Fallback: resolve from npm protocol cache directory (installed via handleNpmProtocol)\n\t\t\t\ttry {\n\t\t\t\t\tconst cacheNpmRoot = path.resolve(\n\t\t\t\t\t\tgetExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory),\n\t\t\t\t\t\t\"node_modules\"\n\t\t\t\t\t);\n\n\t\t\t\t\tconst packagePath = path.resolve(cacheNpmRoot, moduleName);\n\t\t\t\t\tconst mainFile = await resolvePackageEntryPoint(packagePath, moduleName);\n\t\t\t\t\tconst modulePath = path.resolve(packagePath, mainFile);\n\t\t\t\t\tconst exists = await fileExists(modulePath);\n\t\t\t\t\tif (exists) {\n\t\t\t\t\t\tresolvedPath = modulePath;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// No cached resolution either; fall through\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Common module loading and caching logic\n\t\tif (resolvedPath) {\n\t\t\tconst module = await import(createModuleImportUrl(resolvedPath));\n\t\t\tmoduleCache[moduleName] = module;\n\t\t\treturn {\n\t\t\t\tmodule,\n\t\t\t\tuseDefault: false\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tmodule: undefined,\n\t\t\tuseDefault: true\n\t\t};\n\t});\n}\n"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { GeneralError, I18n, Is } from "@twin.org/core";
|
|
4
|
+
import { Engine } from "@twin.org/engine";
|
|
5
|
+
import { FileStateStorage } from "@twin.org/engine-core";
|
|
6
|
+
import { EngineCoreFactory } from "@twin.org/engine-models";
|
|
7
|
+
import { EngineServer } from "@twin.org/engine-server";
|
|
8
|
+
import { BlobStorageConnectorType, EntityStorageConnectorType } from "@twin.org/engine-types";
|
|
9
|
+
import { bootstrap } from "./bootstrap.js";
|
|
10
|
+
import { extensionsInitialiseEngine, extensionsInitialiseEngineServer, shutdownExtensions } from "./builders/extensionsBuilder.js";
|
|
11
|
+
let isStarted = false;
|
|
12
|
+
/**
|
|
13
|
+
* Start the engine server.
|
|
14
|
+
* @param nodeOptions Optional run options for the engine server.
|
|
15
|
+
* @param nodeEngineConfig The configuration for the engine server.
|
|
16
|
+
* @param envVars The environment variables.
|
|
17
|
+
* @param contextIdKeys The context ID keys.
|
|
18
|
+
* @returns The engine server.
|
|
19
|
+
*/
|
|
20
|
+
export async function start(nodeOptions, nodeEngineConfig, envVars, contextIdKeys) {
|
|
21
|
+
const entityStorageConnectorType = envVars.entityStorageConnectorType?.split(",") ?? [];
|
|
22
|
+
const blobStorageConnectorType = envVars.blobStorageConnectorType?.split(",") ?? [];
|
|
23
|
+
// If the blob storage or entity storage is configured with file connectors
|
|
24
|
+
// then we need to make sure the storageFileRoot is set
|
|
25
|
+
if ((entityStorageConnectorType.includes(EntityStorageConnectorType.File) ||
|
|
26
|
+
blobStorageConnectorType.includes(BlobStorageConnectorType.File) ||
|
|
27
|
+
Is.empty(nodeOptions?.stateStorage)) &&
|
|
28
|
+
!Is.stringValue(envVars.storageFileRoot)) {
|
|
29
|
+
throw new GeneralError("node", "storageFileRootNotSet", {
|
|
30
|
+
storageFileRoot: `${nodeOptions?.envPrefix ?? ""}STORAGE_FILE_ROOT`
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Create the engine instance using file state storage unless one is configured in options
|
|
34
|
+
const engine = new Engine({
|
|
35
|
+
config: nodeEngineConfig,
|
|
36
|
+
stateStorage: nodeOptions?.stateStorage ?? new FileStateStorage(envVars.stateFilename ?? ""),
|
|
37
|
+
customBootstrap: async (core, engineContext) => bootstrap(core, engineContext, envVars)
|
|
38
|
+
});
|
|
39
|
+
if (Is.arrayValue(contextIdKeys)) {
|
|
40
|
+
const uniqueContextIdKeys = Array.from(new Set(contextIdKeys));
|
|
41
|
+
for (const contextIdKey of uniqueContextIdKeys) {
|
|
42
|
+
engine.addContextIdKey(contextIdKey);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Construct the server with the engine.
|
|
46
|
+
const server = new EngineServer({ engineCore: engine });
|
|
47
|
+
// Extend the engine.
|
|
48
|
+
if (Is.function(nodeOptions?.extendEngine)) {
|
|
49
|
+
await engine.logInfo(I18n.formatMessage("node.extendingEngine"));
|
|
50
|
+
await nodeOptions.extendEngine(engine);
|
|
51
|
+
}
|
|
52
|
+
await extensionsInitialiseEngine(envVars, engine);
|
|
53
|
+
// Extend the engine server.
|
|
54
|
+
if (Is.function(nodeOptions?.extendEngineServer)) {
|
|
55
|
+
await engine.logInfo(I18n.formatMessage("node.extendingEngineServer"));
|
|
56
|
+
await nodeOptions?.extendEngineServer(server);
|
|
57
|
+
}
|
|
58
|
+
await extensionsInitialiseEngineServer(envVars, engine, server);
|
|
59
|
+
// Need to register the engine with the factory so that background tasks
|
|
60
|
+
// can clone it to spawn new instances.
|
|
61
|
+
EngineCoreFactory.register("engine", () => engine);
|
|
62
|
+
// Start the server, which also starts the engine.
|
|
63
|
+
isStarted = await server.start();
|
|
64
|
+
if (isStarted) {
|
|
65
|
+
return {
|
|
66
|
+
engine,
|
|
67
|
+
server,
|
|
68
|
+
shutdown: async () => {
|
|
69
|
+
if (isStarted) {
|
|
70
|
+
isStarted = false;
|
|
71
|
+
await shutdownExtensions(envVars, engine);
|
|
72
|
+
await server.stop();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, I18n, Is } from \"@twin.org/core\";\nimport { Engine } from \"@twin.org/engine\";\nimport { FileStateStorage } from \"@twin.org/engine-core\";\nimport { EngineCoreFactory } from \"@twin.org/engine-models\";\nimport { EngineServer } from \"@twin.org/engine-server\";\nimport type { IEngineServerConfig } from \"@twin.org/engine-server-types\";\nimport { BlobStorageConnectorType, EntityStorageConnectorType } from \"@twin.org/engine-types\";\nimport { bootstrap } from \"./bootstrap.js\";\nimport {\n\textensionsInitialiseEngine,\n\textensionsInitialiseEngineServer,\n\tshutdownExtensions\n} from \"./builders/extensionsBuilder.js\";\nimport type { INodeEngineConfig } from \"./models/INodeEngineConfig.js\";\nimport type { INodeEngineState } from \"./models/INodeEngineState.js\";\nimport type { INodeEnvironmentVariables } from \"./models/INodeEnvironmentVariables.js\";\nimport type { INodeOptions } from \"./models/INodeOptions.js\";\n\nlet isStarted = false;\n\n/**\n * Start the engine server.\n * @param nodeOptions Optional run options for the engine server.\n * @param nodeEngineConfig The configuration for the engine server.\n * @param envVars The environment variables.\n * @param contextIdKeys The context ID keys.\n * @returns The engine server.\n */\nexport async function start(\n\tnodeOptions: INodeOptions | undefined,\n\tnodeEngineConfig: INodeEngineConfig,\n\tenvVars: INodeEnvironmentVariables,\n\tcontextIdKeys?: string[]\n): Promise<\n\t| {\n\t\t\tengine: Engine<IEngineServerConfig, INodeEngineState>;\n\t\t\tserver: EngineServer;\n\t\t\tshutdown: () => Promise<void>;\n\t }\n\t| undefined\n> {\n\tconst entityStorageConnectorType = envVars.entityStorageConnectorType?.split(\",\") ?? [];\n\tconst blobStorageConnectorType = envVars.blobStorageConnectorType?.split(\",\") ?? [];\n\n\t// If the blob storage or entity storage is configured with file connectors\n\t// then we need to make sure the storageFileRoot is set\n\tif (\n\t\t(entityStorageConnectorType.includes(EntityStorageConnectorType.File) ||\n\t\t\tblobStorageConnectorType.includes(BlobStorageConnectorType.File) ||\n\t\t\tIs.empty(nodeOptions?.stateStorage)) &&\n\t\t!Is.stringValue(envVars.storageFileRoot)\n\t) {\n\t\tthrow new GeneralError(\"node\", \"storageFileRootNotSet\", {\n\t\t\tstorageFileRoot: `${nodeOptions?.envPrefix ?? \"\"}STORAGE_FILE_ROOT`\n\t\t});\n\t}\n\n\t// Create the engine instance using file state storage unless one is configured in options\n\tconst engine = new Engine<IEngineServerConfig, INodeEngineState>({\n\t\tconfig: nodeEngineConfig,\n\t\tstateStorage: nodeOptions?.stateStorage ?? new FileStateStorage(envVars.stateFilename ?? \"\"),\n\t\tcustomBootstrap: async (core, engineContext) => bootstrap(core, engineContext, envVars)\n\t});\n\n\tif (Is.arrayValue(contextIdKeys)) {\n\t\tconst uniqueContextIdKeys = Array.from(new Set(contextIdKeys));\n\t\tfor (const contextIdKey of uniqueContextIdKeys) {\n\t\t\tengine.addContextIdKey(contextIdKey);\n\t\t}\n\t}\n\n\t// Construct the server with the engine.\n\tconst server = new EngineServer({ engineCore: engine });\n\n\t// Extend the engine.\n\tif (Is.function(nodeOptions?.extendEngine)) {\n\t\tawait engine.logInfo(I18n.formatMessage(\"node.extendingEngine\"));\n\t\tawait nodeOptions.extendEngine(engine);\n\t}\n\n\tawait extensionsInitialiseEngine(envVars, engine);\n\n\t// Extend the engine server.\n\tif (Is.function(nodeOptions?.extendEngineServer)) {\n\t\tawait engine.logInfo(I18n.formatMessage(\"node.extendingEngineServer\"));\n\t\tawait nodeOptions?.extendEngineServer(server);\n\t}\n\n\tawait extensionsInitialiseEngineServer(envVars, engine, server);\n\n\t// Need to register the engine with the factory so that background tasks\n\t// can clone it to spawn new instances.\n\tEngineCoreFactory.register(\"engine\", () => engine);\n\n\t// Start the server, which also starts the engine.\n\tisStarted = await server.start();\n\n\tif (isStarted) {\n\t\treturn {\n\t\t\tengine,\n\t\t\tserver,\n\t\t\tshutdown: async () => {\n\t\t\t\tif (isStarted) {\n\t\t\t\t\tisStarted = false;\n\t\t\t\t\tawait shutdownExtensions(envVars, engine);\n\t\t\t\t\tawait server.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n"]}
|