@twin.org/node-core 0.0.2-next.9 → 0.0.3-next.10
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/builders/engineEnvBuilder.js +1132 -0
- package/dist/es/builders/engineEnvBuilder.js.map +1 -0
- package/dist/es/builders/engineServerEnvBuilder.js +206 -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/cli.js +255 -0
- package/dist/es/cli.js.map +1 -0
- package/dist/es/commands/bootstrapLegacy.js +175 -0
- package/dist/es/commands/bootstrapLegacy.js.map +1 -0
- package/dist/es/commands/help.js +85 -0
- package/dist/es/commands/help.js.map +1 -0
- package/dist/es/commands/identityCreate.js +310 -0
- package/dist/es/commands/identityCreate.js.map +1 -0
- package/dist/es/commands/identityImports.js +76 -0
- package/dist/es/commands/identityImports.js.map +1 -0
- package/dist/es/commands/identityVerifiableCredentialCreate.js +140 -0
- package/dist/es/commands/identityVerifiableCredentialCreate.js.map +1 -0
- package/dist/es/commands/identityVerificationMethodCreate.js +208 -0
- package/dist/es/commands/identityVerificationMethodCreate.js.map +1 -0
- package/dist/es/commands/identityVerificationMethodImport.js +120 -0
- package/dist/es/commands/identityVerificationMethodImport.js.map +1 -0
- package/dist/es/commands/nodeSetIdentity.js +51 -0
- package/dist/es/commands/nodeSetIdentity.js.map +1 -0
- package/dist/es/commands/nodeSetTenant.js +49 -0
- package/dist/es/commands/nodeSetTenant.js.map +1 -0
- package/dist/es/commands/tenantCreate.js +120 -0
- package/dist/es/commands/tenantCreate.js.map +1 -0
- package/dist/es/commands/tenantImport.js +78 -0
- package/dist/es/commands/tenantImport.js.map +1 -0
- package/dist/es/commands/userCreate.js +197 -0
- package/dist/es/commands/userCreate.js.map +1 -0
- package/dist/es/commands/vaultKeyCreate.js +185 -0
- package/dist/es/commands/vaultKeyCreate.js.map +1 -0
- package/dist/es/commands/vaultKeyImport.js +98 -0
- package/dist/es/commands/vaultKeyImport.js.map +1 -0
- package/dist/es/defaults.js +28 -0
- package/dist/es/defaults.js.map +1 -0
- package/dist/es/index.js +27 -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/ICliArgs.js +4 -0
- package/dist/es/models/ICliArgs.js.map +1 -0
- package/dist/es/models/ICliCommand.js +2 -0
- package/dist/es/models/ICliCommand.js.map +1 -0
- package/dist/es/models/ICliCommandDefinition.js +2 -0
- package/dist/es/models/ICliCommandDefinition.js.map +1 -0
- package/dist/es/models/ICliCommandDefinitionParam.js +2 -0
- package/dist/es/models/ICliCommandDefinitionParam.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/cliCommandParamType.js +4 -0
- package/dist/es/models/cliCommandParamType.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/node.js +285 -0
- package/dist/es/node.js.map +1 -0
- package/dist/es/start.js +128 -0
- package/dist/es/start.js.map +1 -0
- package/dist/es/utils.js +397 -0
- package/dist/es/utils.js.map +1 -0
- package/dist/types/builders/engineEnvBuilder.d.ts +6 -2
- package/dist/types/builders/engineServerEnvBuilder.d.ts +7 -3
- package/dist/types/builders/extensionsBuilder.d.ts +32 -0
- package/dist/types/cli.d.ts +66 -0
- package/dist/types/commands/bootstrapLegacy.d.ts +66 -0
- package/dist/types/commands/help.d.ts +23 -0
- package/dist/types/commands/identityCreate.d.ts +39 -0
- package/dist/types/commands/identityImports.d.ts +24 -0
- package/dist/types/commands/identityVerifiableCredentialCreate.d.ts +43 -0
- package/dist/types/commands/identityVerificationMethodCreate.d.ts +47 -0
- package/dist/types/commands/identityVerificationMethodImport.d.ts +31 -0
- package/dist/types/commands/nodeSetIdentity.d.ts +22 -0
- package/dist/types/commands/nodeSetTenant.d.ts +22 -0
- package/dist/types/commands/tenantCreate.d.ts +35 -0
- package/dist/types/commands/tenantImport.d.ts +24 -0
- package/dist/types/commands/userCreate.d.ts +46 -0
- package/dist/types/commands/vaultKeyCreate.d.ts +43 -0
- package/dist/types/commands/vaultKeyImport.d.ts +28 -0
- package/dist/types/defaults.d.ts +16 -0
- package/dist/types/index.d.ts +24 -11
- package/dist/types/models/ICacheMetadata.d.ts +17 -0
- package/dist/types/models/ICliArgs.d.ts +20 -0
- package/dist/types/models/ICliCommand.d.ts +17 -0
- package/dist/types/models/ICliCommandDefinition.d.ts +46 -0
- package/dist/types/models/ICliCommandDefinitionParam.d.ts +35 -0
- package/dist/types/models/IEngineEnvironmentVariables.d.ts +154 -37
- 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 +14 -0
- package/dist/types/models/INodeEnvironmentVariables.d.ts +17 -15
- package/dist/types/models/INodeOptions.d.ts +14 -3
- package/dist/types/models/IProtocolHandlerResult.d.ts +13 -0
- package/dist/types/models/cliCommandParamType.d.ts +4 -0
- package/dist/types/models/moduleProtocol.d.ts +29 -0
- package/dist/types/models/nodeExtensionMethods.d.ts +27 -0
- package/dist/types/node.d.ts +21 -7
- package/dist/types/start.d.ts +25 -0
- package/dist/types/utils.d.ts +83 -6
- package/docs/changelog.md +241 -0
- package/docs/detailed-guide.md +129 -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/constructCliCommand.md +27 -0
- package/docs/reference/functions/createModuleImportUrl.md +21 -0
- package/docs/reference/functions/directoryExists.md +19 -0
- package/docs/reference/functions/executeCommand.md +29 -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/getEnvDefaults.md +19 -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/initCli.md +27 -0
- package/docs/reference/functions/isCacheExpired.md +31 -0
- package/docs/reference/functions/overrideModuleImport.md +8 -2
- package/docs/reference/functions/parseCommandLineArgs.md +19 -0
- package/docs/reference/functions/parseModuleProtocol.md +19 -0
- package/docs/reference/functions/processEnvOptions.md +27 -0
- package/docs/reference/functions/registerCommands.md +9 -0
- package/docs/reference/functions/resolvePackageEntryPoint.md +32 -0
- package/docs/reference/functions/run.md +9 -3
- package/docs/reference/functions/shutdownExtensions.md +25 -0
- package/docs/reference/functions/start.md +17 -5
- package/docs/reference/functions/substituteEnvOptions.md +25 -0
- package/docs/reference/index.md +47 -10
- package/docs/reference/interfaces/ICacheMetadata.md +27 -0
- package/docs/reference/interfaces/ICliArgs.md +35 -0
- package/docs/reference/interfaces/ICliCommand.md +23 -0
- package/docs/reference/interfaces/ICliCommandDefinition.md +101 -0
- package/docs/reference/interfaces/ICliCommandDefinitionParam.md +65 -0
- package/docs/reference/interfaces/IEngineEnvironmentVariables.md +303 -60
- package/docs/reference/interfaces/IEngineServerEnvironmentVariables.md +1737 -5
- package/docs/reference/interfaces/IModuleProtocol.md +27 -0
- package/docs/reference/interfaces/INodeEngineConfig.md +7 -0
- package/docs/reference/interfaces/INodeEngineState.md +23 -0
- package/docs/reference/interfaces/INodeEnvironmentVariables.md +599 -198
- package/docs/reference/interfaces/INodeOptions.md +27 -3
- package/docs/reference/interfaces/IProtocolHandlerResult.md +19 -0
- package/docs/reference/type-aliases/CliCommandParamType.md +5 -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/CONTEXT_ID_HANDLER_FEATURE_DID.md +3 -0
- package/docs/reference/variables/CONTEXT_ID_HANDLER_FEATURE_TENANT.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/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 +479 -28
- package/package.json +29 -9
- package/dist/cjs/index.cjs +0 -1910
- package/dist/esm/index.mjs +0 -1870
- package/dist/types/bootstrap.d.ts +0 -59
- package/dist/types/models/nodeFeatures.d.ts +0 -21
- package/dist/types/server.d.ts +0 -17
- package/docs/reference/functions/bootstrap.md +0 -29
- package/docs/reference/functions/bootstrapAuth.md +0 -35
- package/docs/reference/functions/bootstrapBlobEncryption.md +0 -35
- package/docs/reference/functions/bootstrapImmutableProofMethod.md +0 -35
- package/docs/reference/functions/bootstrapNodeIdentity.md +0 -35
- package/docs/reference/functions/bootstrapNodeUser.md +0 -35
- package/docs/reference/functions/bootstrapSynchronisedStorage.md +0 -35
- package/docs/reference/functions/getFeatures.md +0 -19
- package/docs/reference/type-aliases/NodeFeatures.md +0 -5
- package/docs/reference/variables/NodeFeatures.md +0 -25
package/dist/es/utils.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { mkdir, readdir, readFile, rename, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { get as httpsGet } from "node:https";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { CLIDisplay } from "@twin.org/cli-core";
|
|
8
|
+
import { BaseError, Converter, GeneralError, I18n } from "@twin.org/core";
|
|
9
|
+
import { Sha256 } from "@twin.org/crypto";
|
|
10
|
+
import { ModuleHelper } from "@twin.org/modules";
|
|
11
|
+
import { ModuleProtocol } from "./models/moduleProtocol.js";
|
|
12
|
+
/**
|
|
13
|
+
* Initialise the locales for the application.
|
|
14
|
+
* @param localesDirectory The directory containing the locales.
|
|
15
|
+
*/
|
|
16
|
+
export async function initialiseLocales(localesDirectory) {
|
|
17
|
+
const localesFile = path.resolve(path.join(localesDirectory, "en.json"));
|
|
18
|
+
CLIDisplay.value("Locales File", localesFile);
|
|
19
|
+
if (await fileExists(localesFile)) {
|
|
20
|
+
const enLangContent = await readFile(localesFile, "utf8");
|
|
21
|
+
I18n.addDictionary("en", JSON.parse(enLangContent));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
CLIDisplay.error(`Locales file not found: ${localesFile}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the directory where the application is being executed.
|
|
29
|
+
* @returns The execution directory.
|
|
30
|
+
*/
|
|
31
|
+
export function getExecutionDirectory() {
|
|
32
|
+
return process.cwd();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Does the specified file exist.
|
|
36
|
+
* @param filename The filename to check for existence.
|
|
37
|
+
* @returns True if the file exists.
|
|
38
|
+
*/
|
|
39
|
+
export async function fileExists(filename) {
|
|
40
|
+
try {
|
|
41
|
+
const stats = await stat(filename);
|
|
42
|
+
return stats.isFile();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Does the specified directory exist.
|
|
50
|
+
* @param directory The directory to check for existence.
|
|
51
|
+
* @returns True if the directory exists.
|
|
52
|
+
*/
|
|
53
|
+
export async function directoryExists(directory) {
|
|
54
|
+
try {
|
|
55
|
+
const stats = await stat(directory);
|
|
56
|
+
return stats.isDirectory();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the sub folders for the folder.
|
|
64
|
+
* @param directory The directory to get the sub folders.
|
|
65
|
+
* @returns The list of sub folders.
|
|
66
|
+
*/
|
|
67
|
+
export async function getSubFolders(directory) {
|
|
68
|
+
try {
|
|
69
|
+
const dir = await readdir(directory);
|
|
70
|
+
const folders = [];
|
|
71
|
+
for (const dirEntry of dir) {
|
|
72
|
+
const fullPath = path.join(directory, dirEntry);
|
|
73
|
+
const stats = await stat(fullPath);
|
|
74
|
+
if (stats.isDirectory()) {
|
|
75
|
+
folders.push(fullPath);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return folders;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get the files in the directory.
|
|
86
|
+
* @param directory The directory to get the files from.
|
|
87
|
+
* @returns The list of files in the directory.
|
|
88
|
+
*/
|
|
89
|
+
export async function getFiles(directory) {
|
|
90
|
+
try {
|
|
91
|
+
const dir = await readdir(directory);
|
|
92
|
+
const files = [];
|
|
93
|
+
for (const dirEntry of dir) {
|
|
94
|
+
const fullPath = path.join(directory, dirEntry);
|
|
95
|
+
const stats = await stat(fullPath);
|
|
96
|
+
if (stats.isFile()) {
|
|
97
|
+
files.push(fullPath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Load the text file.
|
|
108
|
+
* @param filename The filename of the text file to load.
|
|
109
|
+
* @returns The contents of the text file if it could not be loaded.
|
|
110
|
+
*/
|
|
111
|
+
export async function loadTextFile(filename) {
|
|
112
|
+
return readFile(filename, "utf8");
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Load the JSON file.
|
|
116
|
+
* @param filename The filename of the JSON file to load.
|
|
117
|
+
* @returns The contents of the JSON file or null if it could not be loaded.
|
|
118
|
+
*/
|
|
119
|
+
export async function loadJsonFile(filename) {
|
|
120
|
+
const content = await loadTextFile(filename);
|
|
121
|
+
return JSON.parse(content);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parse the protocol from a module name.
|
|
125
|
+
* @param moduleName The module name to parse.
|
|
126
|
+
* @returns The parsed protocol information.
|
|
127
|
+
*/
|
|
128
|
+
export function parseModuleProtocol(moduleName) {
|
|
129
|
+
const trimmed = moduleName.trim();
|
|
130
|
+
if (trimmed.startsWith("npm:")) {
|
|
131
|
+
return {
|
|
132
|
+
protocol: ModuleProtocol.Npm,
|
|
133
|
+
identifier: trimmed.slice(4),
|
|
134
|
+
original: trimmed
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (trimmed.startsWith("https://")) {
|
|
138
|
+
return {
|
|
139
|
+
protocol: ModuleProtocol.Https,
|
|
140
|
+
identifier: trimmed,
|
|
141
|
+
original: trimmed
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (trimmed.startsWith("http://")) {
|
|
145
|
+
return {
|
|
146
|
+
protocol: ModuleProtocol.Http,
|
|
147
|
+
identifier: trimmed,
|
|
148
|
+
original: trimmed
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (trimmed.startsWith("file://")) {
|
|
152
|
+
return {
|
|
153
|
+
protocol: ModuleProtocol.Local,
|
|
154
|
+
identifier: trimmed,
|
|
155
|
+
original: trimmed
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (ModuleHelper.isLocalModule(trimmed)) {
|
|
159
|
+
return {
|
|
160
|
+
protocol: ModuleProtocol.Local,
|
|
161
|
+
identifier: trimmed,
|
|
162
|
+
original: trimmed
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
protocol: ModuleProtocol.Default,
|
|
167
|
+
identifier: trimmed,
|
|
168
|
+
original: trimmed
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Hash a URL to create a safe filename.
|
|
173
|
+
* @param url The URL to hash.
|
|
174
|
+
* @returns A hashed filename safe for the filesystem.
|
|
175
|
+
*/
|
|
176
|
+
export function hashUrl(url) {
|
|
177
|
+
const urlBytes = Converter.utf8ToBytes(url);
|
|
178
|
+
const hashBytes = Sha256.sum256(urlBytes);
|
|
179
|
+
const hash = Converter.bytesToHex(hashBytes);
|
|
180
|
+
const ext = path.extname(new URL(url).pathname);
|
|
181
|
+
return `${hash}${ext}`;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get the extensions cache directory.
|
|
185
|
+
* @param executionDirectory The execution directory.
|
|
186
|
+
* @param protocol The protocol type for subdirectory organization.
|
|
187
|
+
* @param cacheDirectory The cache directory base path.
|
|
188
|
+
* @returns The cache directory path.
|
|
189
|
+
*/
|
|
190
|
+
export function getExtensionsCacheDir(executionDirectory, protocol, cacheDirectory) {
|
|
191
|
+
// Resolve to absolute path to ensure consistent behavior
|
|
192
|
+
const absoluteDir = path.resolve(executionDirectory);
|
|
193
|
+
const baseDir = cacheDirectory ?? ".tmp";
|
|
194
|
+
return path.join(absoluteDir, baseDir, "extensions", protocol);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Handle the npm: protocol by installing the package if needed.
|
|
198
|
+
* @param packageName The npm package name (without npm: prefix).
|
|
199
|
+
* @param executionDirectory The execution directory.
|
|
200
|
+
* @param cacheDirectory The cache directory base path.
|
|
201
|
+
* @returns The resolved path to the installed module.
|
|
202
|
+
*/
|
|
203
|
+
export async function handleNpmProtocol(packageName, executionDirectory, cacheDirectory) {
|
|
204
|
+
const cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory);
|
|
205
|
+
// Extract just the package name (without version) for the directory
|
|
206
|
+
// e.g. "picocolors@1.0.0" becomes "picocolors"
|
|
207
|
+
// e.g. "@scope/package@1.0.0" becomes "@scope/package"
|
|
208
|
+
const lastAtIndex = packageName.lastIndexOf("@");
|
|
209
|
+
const packageNameOnly = lastAtIndex > 0 ? packageName.slice(0, lastAtIndex) : packageName;
|
|
210
|
+
const packageDir = path.join(cacheDir, "node_modules", packageNameOnly);
|
|
211
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
212
|
+
const exists = await fileExists(packageJsonPath);
|
|
213
|
+
if (exists) {
|
|
214
|
+
const mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);
|
|
215
|
+
const modulePath = path.join(packageDir, mainFile);
|
|
216
|
+
return {
|
|
217
|
+
resolvedPath: modulePath,
|
|
218
|
+
cached: true
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
await mkdir(cacheDir, { recursive: true });
|
|
222
|
+
CLIDisplay.task(I18n.formatMessage("node.extensionNpmInstalling"), packageName);
|
|
223
|
+
try {
|
|
224
|
+
// Always pipe stdio to comply with env access restrictions in tests/lint
|
|
225
|
+
const stdio = "pipe";
|
|
226
|
+
execSync(`npm install ${packageName} --prefix "${cacheDir}" --no-save --no-package-lock`, {
|
|
227
|
+
cwd: cacheDir,
|
|
228
|
+
stdio
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
throw new GeneralError("node", "extensionNpmInstallFailed", {
|
|
233
|
+
package: packageName
|
|
234
|
+
}, BaseError.fromError(err));
|
|
235
|
+
}
|
|
236
|
+
const mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);
|
|
237
|
+
const modulePath = path.join(packageDir, mainFile);
|
|
238
|
+
return {
|
|
239
|
+
resolvedPath: modulePath,
|
|
240
|
+
cached: false
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Check if a cached file has expired based on TTL and force refresh settings.
|
|
245
|
+
* @param metadataPath Path to the cache metadata file.
|
|
246
|
+
* @param ttlHours Time to live in hours.
|
|
247
|
+
* @param forceRefresh Whether to force refresh regardless of TTL.
|
|
248
|
+
* @returns True if the cache is expired or should be refreshed.
|
|
249
|
+
*/
|
|
250
|
+
export async function isCacheExpired(metadataPath, ttlHours, forceRefresh) {
|
|
251
|
+
if (forceRefresh) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const metadata = await loadJsonFile(metadataPath);
|
|
256
|
+
const ttlMillis = ttlHours * 60 * 60 * 1000;
|
|
257
|
+
const expireTime = metadata.downloadedAt + ttlMillis;
|
|
258
|
+
return Date.now() > expireTime;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// If metadata doesn't exist or is corrupted, consider expired
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Handle the https: protocol by downloading the module if needed.
|
|
267
|
+
* @param url The HTTPS URL to download from.
|
|
268
|
+
* @param executionDirectory The execution directory.
|
|
269
|
+
* @param maxSizeMb The maximum size in MB for the download.
|
|
270
|
+
* @param cacheDirectory The cache directory base path.
|
|
271
|
+
* @param ttlHours TTL in hours for cache expiration.
|
|
272
|
+
* @param forceRefresh Whether to force refresh the cache.
|
|
273
|
+
* @returns The resolved path to the downloaded module.
|
|
274
|
+
*/
|
|
275
|
+
export async function handleHttpsProtocol(url, executionDirectory, maxSizeMb, cacheDirectory, ttlHours, forceRefresh) {
|
|
276
|
+
const effectiveTtlHours = ttlHours ?? 24;
|
|
277
|
+
const effectiveForceRefresh = forceRefresh ?? false;
|
|
278
|
+
const cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Https, cacheDirectory);
|
|
279
|
+
const filename = hashUrl(url);
|
|
280
|
+
const cachedPath = path.join(cacheDir, filename);
|
|
281
|
+
const metadataPath = `${cachedPath}.meta`;
|
|
282
|
+
const exists = await fileExists(cachedPath);
|
|
283
|
+
if (exists) {
|
|
284
|
+
const expired = await isCacheExpired(metadataPath, effectiveTtlHours, effectiveForceRefresh);
|
|
285
|
+
if (!expired) {
|
|
286
|
+
return {
|
|
287
|
+
resolvedPath: cachedPath,
|
|
288
|
+
cached: true
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (effectiveForceRefresh) {
|
|
292
|
+
CLIDisplay.warning(I18n.formatMessage("node.extensionForceRefresh", { url }));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
CLIDisplay.task(I18n.formatMessage("node.extensionCacheExpired", { url }));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
CLIDisplay.warning(I18n.formatMessage("node.extensionSecurityWarning", { url }));
|
|
299
|
+
CLIDisplay.task(I18n.formatMessage("node.extensionHttpsDownloading"), url);
|
|
300
|
+
await mkdir(cacheDir, { recursive: true });
|
|
301
|
+
const maxSizeBytes = maxSizeMb * 1024 * 1024;
|
|
302
|
+
let downloadedSize = 0;
|
|
303
|
+
const chunks = [];
|
|
304
|
+
try {
|
|
305
|
+
await new Promise((resolve, reject) => {
|
|
306
|
+
httpsGet(url, response => {
|
|
307
|
+
if (response.statusCode !== 200) {
|
|
308
|
+
reject(new GeneralError("node", "extensionDownloadFailed", {
|
|
309
|
+
url,
|
|
310
|
+
status: response.statusCode ?? 0
|
|
311
|
+
}));
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
response.on("data", (chunk) => {
|
|
315
|
+
downloadedSize += chunk.length;
|
|
316
|
+
if (downloadedSize > maxSizeBytes) {
|
|
317
|
+
response.destroy();
|
|
318
|
+
reject(new GeneralError("node", "extensionSizeLimitExceeded", {
|
|
319
|
+
size: downloadedSize,
|
|
320
|
+
limit: maxSizeBytes
|
|
321
|
+
}));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
chunks.push(chunk);
|
|
325
|
+
});
|
|
326
|
+
response.on("end", () => {
|
|
327
|
+
resolve();
|
|
328
|
+
});
|
|
329
|
+
response.on("error", err => {
|
|
330
|
+
reject(BaseError.fromError(err));
|
|
331
|
+
});
|
|
332
|
+
}).on("error", err => {
|
|
333
|
+
reject(BaseError.fromError(err));
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
throw new GeneralError("node", "extensionDownloadFailed", {
|
|
339
|
+
url
|
|
340
|
+
}, BaseError.fromError(err));
|
|
341
|
+
}
|
|
342
|
+
const tempPath = `${cachedPath}.tmp`;
|
|
343
|
+
await writeFile(tempPath, Buffer.concat(chunks));
|
|
344
|
+
// Atomic move from temp to final location
|
|
345
|
+
await rename(tempPath, cachedPath);
|
|
346
|
+
// Save metadata for TTL tracking
|
|
347
|
+
const metadata = {
|
|
348
|
+
downloadedAt: Date.now(),
|
|
349
|
+
url,
|
|
350
|
+
size: Buffer.concat(chunks).length
|
|
351
|
+
};
|
|
352
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
353
|
+
return {
|
|
354
|
+
resolvedPath: cachedPath,
|
|
355
|
+
cached: false
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Resolve the main entry point from a package directory using Node.js resolution with fallback.
|
|
360
|
+
* Uses require.resolve() when possible for standard Node.js behavior, with manual fallback.
|
|
361
|
+
* @param packagePath The absolute path to the package directory.
|
|
362
|
+
* @param packageName The package name for require.resolve().
|
|
363
|
+
* @param fallback The fallback file name if no entry point is found.
|
|
364
|
+
* @returns The resolved entry point file name (relative to package directory).
|
|
365
|
+
*/
|
|
366
|
+
export async function resolvePackageEntryPoint(packagePath, packageName, fallback = "index.js") {
|
|
367
|
+
try {
|
|
368
|
+
// Try require.resolve() first - handles exports, main, module automatically
|
|
369
|
+
const resolvedPath = require.resolve(packageName, { paths: [path.dirname(packagePath)] });
|
|
370
|
+
// Convert absolute path back to relative filename within package
|
|
371
|
+
const relativePath = path.relative(packagePath, resolvedPath);
|
|
372
|
+
return relativePath ?? fallback;
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
// Fallback to manual package.json parsing if require.resolve fails
|
|
376
|
+
try {
|
|
377
|
+
const packageJsonPath = path.join(packagePath, "package.json");
|
|
378
|
+
const packageJsonContent = await loadJsonFile(packageJsonPath);
|
|
379
|
+
// Future: Could expand exports field support here
|
|
380
|
+
return packageJsonContent.module ?? packageJsonContent.main ?? fallback;
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return fallback;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Convert a file path to an import-compatible URL for cross-platform module loading.
|
|
389
|
+
* On Windows, adds the 'file://' protocol prefix required for dynamic imports.
|
|
390
|
+
* On other platforms, returns the path unchanged.
|
|
391
|
+
* @param filePath The absolute file path to convert.
|
|
392
|
+
* @returns A URL string compatible with dynamic import().
|
|
393
|
+
*/
|
|
394
|
+
export function createModuleImportUrl(filePath) {
|
|
395
|
+
return process.platform === "win32" ? `file://${filePath}` : filePath;
|
|
396
|
+
}
|
|
397
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.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 { mkdir, readdir, readFile, rename, stat, writeFile } from \"node:fs/promises\";\nimport { get as httpsGet } from \"node:https\";\nimport path from \"node:path\";\nimport { CLIDisplay } from \"@twin.org/cli-core\";\nimport { BaseError, Converter, GeneralError, I18n, type ILocaleDictionary } from \"@twin.org/core\";\nimport { Sha256 } from \"@twin.org/crypto\";\nimport { ModuleHelper } from \"@twin.org/modules\";\nimport type { ICacheMetadata } from \"./models/ICacheMetadata.js\";\nimport type { IModuleProtocol } from \"./models/IModuleProtocol.js\";\nimport type { IProtocolHandlerResult } from \"./models/IProtocolHandlerResult.js\";\nimport { ModuleProtocol } from \"./models/moduleProtocol.js\";\n\n/**\n * Initialise the locales for the application.\n * @param localesDirectory The directory containing the locales.\n */\nexport async function initialiseLocales(localesDirectory: string): Promise<void> {\n\tconst localesFile = path.resolve(path.join(localesDirectory, \"en.json\"));\n\tCLIDisplay.value(\"Locales File\", localesFile);\n\tif (await fileExists(localesFile)) {\n\t\tconst enLangContent = await readFile(localesFile, \"utf8\");\n\t\tI18n.addDictionary(\"en\", JSON.parse(enLangContent) as ILocaleDictionary);\n\t} else {\n\t\tCLIDisplay.error(`Locales file not found: ${localesFile}`);\n\t}\n}\n\n/**\n * Get the directory where the application is being executed.\n * @returns The execution directory.\n */\nexport function getExecutionDirectory(): string {\n\treturn process.cwd();\n}\n\n/**\n * Does the specified file exist.\n * @param filename The filename to check for existence.\n * @returns True if the file exists.\n */\nexport async function fileExists(filename: string): Promise<boolean> {\n\ttry {\n\t\tconst stats = await stat(filename);\n\t\treturn stats.isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Does the specified directory exist.\n * @param directory The directory to check for existence.\n * @returns True if the directory exists.\n */\nexport async function directoryExists(directory: string): Promise<boolean> {\n\ttry {\n\t\tconst stats = await stat(directory);\n\t\treturn stats.isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get the sub folders for the folder.\n * @param directory The directory to get the sub folders.\n * @returns The list of sub folders.\n */\nexport async function getSubFolders(directory: string): Promise<string[]> {\n\ttry {\n\t\tconst dir = await readdir(directory);\n\t\tconst folders: string[] = [];\n\t\tfor (const dirEntry of dir) {\n\t\t\tconst fullPath = path.join(directory, dirEntry);\n\t\t\tconst stats = await stat(fullPath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tfolders.push(fullPath);\n\t\t\t}\n\t\t}\n\t\treturn folders;\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Get the files in the directory.\n * @param directory The directory to get the files from.\n * @returns The list of files in the directory.\n */\nexport async function getFiles(directory: string): Promise<string[]> {\n\ttry {\n\t\tconst dir = await readdir(directory);\n\t\tconst files: string[] = [];\n\t\tfor (const dirEntry of dir) {\n\t\t\tconst fullPath = path.join(directory, dirEntry);\n\t\t\tconst stats = await stat(fullPath);\n\t\t\tif (stats.isFile()) {\n\t\t\t\tfiles.push(fullPath);\n\t\t\t}\n\t\t}\n\t\treturn files;\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Load the text file.\n * @param filename The filename of the text file to load.\n * @returns The contents of the text file if it could not be loaded.\n */\nexport async function loadTextFile(filename: string): Promise<string> {\n\treturn readFile(filename, \"utf8\");\n}\n\n/**\n * Load the JSON file.\n * @param filename The filename of the JSON file to load.\n * @returns The contents of the JSON file or null if it could not be loaded.\n */\nexport async function loadJsonFile<T>(filename: string): Promise<T> {\n\tconst content = await loadTextFile(filename);\n\treturn JSON.parse(content) as T;\n}\n\n/**\n * Parse the protocol from a module name.\n * @param moduleName The module name to parse.\n * @returns The parsed protocol information.\n */\nexport function parseModuleProtocol(moduleName: string): IModuleProtocol {\n\tconst trimmed = moduleName.trim();\n\n\tif (trimmed.startsWith(\"npm:\")) {\n\t\treturn {\n\t\t\tprotocol: ModuleProtocol.Npm,\n\t\t\tidentifier: trimmed.slice(4),\n\t\t\toriginal: trimmed\n\t\t};\n\t}\n\n\tif (trimmed.startsWith(\"https://\")) {\n\t\treturn {\n\t\t\tprotocol: ModuleProtocol.Https,\n\t\t\tidentifier: trimmed,\n\t\t\toriginal: trimmed\n\t\t};\n\t}\n\n\tif (trimmed.startsWith(\"http://\")) {\n\t\treturn {\n\t\t\tprotocol: ModuleProtocol.Http,\n\t\t\tidentifier: trimmed,\n\t\t\toriginal: trimmed\n\t\t};\n\t}\n\n\tif (trimmed.startsWith(\"file://\")) {\n\t\treturn {\n\t\t\tprotocol: ModuleProtocol.Local,\n\t\t\tidentifier: trimmed,\n\t\t\toriginal: trimmed\n\t\t};\n\t}\n\n\tif (ModuleHelper.isLocalModule(trimmed)) {\n\t\treturn {\n\t\t\tprotocol: ModuleProtocol.Local,\n\t\t\tidentifier: trimmed,\n\t\t\toriginal: trimmed\n\t\t};\n\t}\n\n\treturn {\n\t\tprotocol: ModuleProtocol.Default,\n\t\tidentifier: trimmed,\n\t\toriginal: trimmed\n\t};\n}\n\n/**\n * Hash a URL to create a safe filename.\n * @param url The URL to hash.\n * @returns A hashed filename safe for the filesystem.\n */\nexport function hashUrl(url: string): string {\n\tconst urlBytes = Converter.utf8ToBytes(url);\n\tconst hashBytes = Sha256.sum256(urlBytes);\n\tconst hash = Converter.bytesToHex(hashBytes);\n\tconst ext = path.extname(new URL(url).pathname);\n\treturn `${hash}${ext}`;\n}\n\n/**\n * Get the extensions cache directory.\n * @param executionDirectory The execution directory.\n * @param protocol The protocol type for subdirectory organization.\n * @param cacheDirectory The cache directory base path.\n * @returns The cache directory path.\n */\nexport function getExtensionsCacheDir(\n\texecutionDirectory: string,\n\tprotocol: ModuleProtocol,\n\tcacheDirectory?: string\n): string {\n\t// Resolve to absolute path to ensure consistent behavior\n\tconst absoluteDir = path.resolve(executionDirectory);\n\tconst baseDir = cacheDirectory ?? \".tmp\";\n\treturn path.join(absoluteDir, baseDir, \"extensions\", protocol);\n}\n\n/**\n * Handle the npm: protocol by installing the package if needed.\n * @param packageName The npm package name (without npm: prefix).\n * @param executionDirectory The execution directory.\n * @param cacheDirectory The cache directory base path.\n * @returns The resolved path to the installed module.\n */\nexport async function handleNpmProtocol(\n\tpackageName: string,\n\texecutionDirectory: string,\n\tcacheDirectory?: string\n): Promise<IProtocolHandlerResult> {\n\tconst cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory);\n\t// Extract just the package name (without version) for the directory\n\t// e.g. \"picocolors@1.0.0\" becomes \"picocolors\"\n\t// e.g. \"@scope/package@1.0.0\" becomes \"@scope/package\"\n\tconst lastAtIndex = packageName.lastIndexOf(\"@\");\n\tconst packageNameOnly = lastAtIndex > 0 ? packageName.slice(0, lastAtIndex) : packageName;\n\tconst packageDir = path.join(cacheDir, \"node_modules\", packageNameOnly);\n\tconst packageJsonPath = path.join(packageDir, \"package.json\");\n\n\tconst exists = await fileExists(packageJsonPath);\n\tif (exists) {\n\t\tconst mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);\n\t\tconst modulePath = path.join(packageDir, mainFile);\n\n\t\treturn {\n\t\t\tresolvedPath: modulePath,\n\t\t\tcached: true\n\t\t};\n\t}\n\n\tawait mkdir(cacheDir, { recursive: true });\n\n\tCLIDisplay.task(I18n.formatMessage(\"node.extensionNpmInstalling\"), packageName);\n\n\ttry {\n\t\t// Always pipe stdio to comply with env access restrictions in tests/lint\n\t\tconst stdio: \"pipe\" | \"inherit\" | \"ignore\" = \"pipe\";\n\t\texecSync(`npm install ${packageName} --prefix \"${cacheDir}\" --no-save --no-package-lock`, {\n\t\t\tcwd: cacheDir,\n\t\t\tstdio\n\t\t});\n\t} catch (err) {\n\t\tthrow new GeneralError(\n\t\t\t\"node\",\n\t\t\t\"extensionNpmInstallFailed\",\n\t\t\t{\n\t\t\t\tpackage: packageName\n\t\t\t},\n\t\t\tBaseError.fromError(err)\n\t\t);\n\t}\n\n\tconst mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);\n\tconst modulePath = path.join(packageDir, mainFile);\n\n\treturn {\n\t\tresolvedPath: modulePath,\n\t\tcached: false\n\t};\n}\n\n/**\n * Check if a cached file has expired based on TTL and force refresh settings.\n * @param metadataPath Path to the cache metadata file.\n * @param ttlHours Time to live in hours.\n * @param forceRefresh Whether to force refresh regardless of TTL.\n * @returns True if the cache is expired or should be refreshed.\n */\nexport async function isCacheExpired(\n\tmetadataPath: string,\n\tttlHours: number,\n\tforceRefresh: boolean\n): Promise<boolean> {\n\tif (forceRefresh) {\n\t\treturn true;\n\t}\n\n\ttry {\n\t\tconst metadata = await loadJsonFile<ICacheMetadata>(metadataPath);\n\t\tconst ttlMillis = ttlHours * 60 * 60 * 1000;\n\t\tconst expireTime = metadata.downloadedAt + ttlMillis;\n\t\treturn Date.now() > expireTime;\n\t} catch {\n\t\t// If metadata doesn't exist or is corrupted, consider expired\n\t\treturn true;\n\t}\n}\n\n/**\n * Handle the https: protocol by downloading the module if needed.\n * @param url The HTTPS URL to download from.\n * @param executionDirectory The execution directory.\n * @param maxSizeMb The maximum size in MB for the download.\n * @param cacheDirectory The cache directory base path.\n * @param ttlHours TTL in hours for cache expiration.\n * @param forceRefresh Whether to force refresh the cache.\n * @returns The resolved path to the downloaded module.\n */\nexport async function handleHttpsProtocol(\n\turl: string,\n\texecutionDirectory: string,\n\tmaxSizeMb: number,\n\tcacheDirectory?: string,\n\tttlHours?: number,\n\tforceRefresh?: boolean\n): Promise<IProtocolHandlerResult> {\n\tconst effectiveTtlHours = ttlHours ?? 24;\n\tconst effectiveForceRefresh = forceRefresh ?? false;\n\tconst cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Https, cacheDirectory);\n\tconst filename = hashUrl(url);\n\tconst cachedPath = path.join(cacheDir, filename);\n\tconst metadataPath = `${cachedPath}.meta`;\n\n\tconst exists = await fileExists(cachedPath);\n\tif (exists) {\n\t\tconst expired = await isCacheExpired(metadataPath, effectiveTtlHours, effectiveForceRefresh);\n\t\tif (!expired) {\n\t\t\treturn {\n\t\t\t\tresolvedPath: cachedPath,\n\t\t\t\tcached: true\n\t\t\t};\n\t\t}\n\n\t\tif (effectiveForceRefresh) {\n\t\t\tCLIDisplay.warning(I18n.formatMessage(\"node.extensionForceRefresh\", { url }));\n\t\t} else {\n\t\t\tCLIDisplay.task(I18n.formatMessage(\"node.extensionCacheExpired\", { url }));\n\t\t}\n\t}\n\n\tCLIDisplay.warning(I18n.formatMessage(\"node.extensionSecurityWarning\", { url }));\n\tCLIDisplay.task(I18n.formatMessage(\"node.extensionHttpsDownloading\"), url);\n\n\tawait mkdir(cacheDir, { recursive: true });\n\n\tconst maxSizeBytes = maxSizeMb * 1024 * 1024;\n\tlet downloadedSize = 0;\n\tconst chunks: Buffer[] = [];\n\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\thttpsGet(url, response => {\n\t\t\t\tif (response.statusCode !== 200) {\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew GeneralError(\"node\", \"extensionDownloadFailed\", {\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\tstatus: response.statusCode ?? 0\n\t\t\t\t\t\t})\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresponse.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tdownloadedSize += chunk.length;\n\n\t\t\t\t\tif (downloadedSize > maxSizeBytes) {\n\t\t\t\t\t\tresponse.destroy();\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew GeneralError(\"node\", \"extensionSizeLimitExceeded\", {\n\t\t\t\t\t\t\t\tsize: downloadedSize,\n\t\t\t\t\t\t\t\tlimit: maxSizeBytes\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tchunks.push(chunk);\n\t\t\t\t});\n\n\t\t\t\tresponse.on(\"end\", () => {\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\n\t\t\t\tresponse.on(\"error\", err => {\n\t\t\t\t\treject(BaseError.fromError(err));\n\t\t\t\t});\n\t\t\t}).on(\"error\", err => {\n\t\t\t\treject(BaseError.fromError(err));\n\t\t\t});\n\t\t});\n\t} catch (err) {\n\t\tthrow new GeneralError(\n\t\t\t\"node\",\n\t\t\t\"extensionDownloadFailed\",\n\t\t\t{\n\t\t\t\turl\n\t\t\t},\n\t\t\tBaseError.fromError(err)\n\t\t);\n\t}\n\n\tconst tempPath = `${cachedPath}.tmp`;\n\tawait writeFile(tempPath, Buffer.concat(chunks));\n\n\t// Atomic move from temp to final location\n\tawait rename(tempPath, cachedPath);\n\n\t// Save metadata for TTL tracking\n\tconst metadata: ICacheMetadata = {\n\t\tdownloadedAt: Date.now(),\n\t\turl,\n\t\tsize: Buffer.concat(chunks).length\n\t};\n\tawait writeFile(metadataPath, JSON.stringify(metadata, null, 2));\n\n\treturn {\n\t\tresolvedPath: cachedPath,\n\t\tcached: false\n\t};\n}\n\n/**\n * Resolve the main entry point from a package directory using Node.js resolution with fallback.\n * Uses require.resolve() when possible for standard Node.js behavior, with manual fallback.\n * @param packagePath The absolute path to the package directory.\n * @param packageName The package name for require.resolve().\n * @param fallback The fallback file name if no entry point is found.\n * @returns The resolved entry point file name (relative to package directory).\n */\nexport async function resolvePackageEntryPoint(\n\tpackagePath: string,\n\tpackageName: string,\n\tfallback = \"index.js\"\n): Promise<string> {\n\ttry {\n\t\t// Try require.resolve() first - handles exports, main, module automatically\n\t\tconst resolvedPath = require.resolve(packageName, { paths: [path.dirname(packagePath)] });\n\t\t// Convert absolute path back to relative filename within package\n\t\tconst relativePath = path.relative(packagePath, resolvedPath);\n\t\treturn relativePath ?? fallback;\n\t} catch {\n\t\t// Fallback to manual package.json parsing if require.resolve fails\n\t\ttry {\n\t\t\tconst packageJsonPath = path.join(packagePath, \"package.json\");\n\t\t\tconst packageJsonContent = await loadJsonFile<{\n\t\t\t\tmodule?: string;\n\t\t\t\tmain?: string;\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\texports?: any;\n\t\t\t}>(packageJsonPath);\n\n\t\t\t// Future: Could expand exports field support here\n\t\t\treturn packageJsonContent.module ?? packageJsonContent.main ?? fallback;\n\t\t} catch {\n\t\t\treturn fallback;\n\t\t}\n\t}\n}\n\n/**\n * Convert a file path to an import-compatible URL for cross-platform module loading.\n * On Windows, adds the 'file://' protocol prefix required for dynamic imports.\n * On other platforms, returns the path unchanged.\n * @param filePath The absolute file path to convert.\n * @returns A URL string compatible with dynamic import().\n */\nexport function createModuleImportUrl(filePath: string): string {\n\treturn process.platform === \"win32\" ? `file://${filePath}` : filePath;\n}\n"]}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { type IEngineConfig } from "@twin.org/engine-types";
|
|
2
|
-
import type { IEngineEnvironmentVariables } from "../models/IEngineEnvironmentVariables";
|
|
2
|
+
import type { IEngineEnvironmentVariables } from "../models/IEngineEnvironmentVariables.js";
|
|
3
3
|
/**
|
|
4
4
|
* Build the engine core configuration from environment variables.
|
|
5
5
|
* @param envVars The environment variables.
|
|
6
|
+
* @param contextIdKeys The context ID keys.
|
|
6
7
|
* @returns The config for the core.
|
|
7
8
|
*/
|
|
8
|
-
export declare function buildEngineConfiguration(envVars: IEngineEnvironmentVariables
|
|
9
|
+
export declare function buildEngineConfiguration(envVars: IEngineEnvironmentVariables, contextIdKeys: {
|
|
10
|
+
key: string;
|
|
11
|
+
requiredHandlerFeatures: string[];
|
|
12
|
+
}[]): Promise<IEngineConfig>;
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import type { IServerInfo } from "@twin.org/api-models";
|
|
2
2
|
import type { IEngineCoreConfig } from "@twin.org/engine-models";
|
|
3
3
|
import { type IEngineServerConfig } from "@twin.org/engine-server-types";
|
|
4
|
-
import type { IEngineServerEnvironmentVariables } from "../models/IEngineServerEnvironmentVariables";
|
|
4
|
+
import type { IEngineServerEnvironmentVariables } from "../models/IEngineServerEnvironmentVariables.js";
|
|
5
5
|
/**
|
|
6
6
|
* Handles the configuration of the server.
|
|
7
7
|
* @param envVars The environment variables for the engine server.
|
|
8
|
+
* @param availableContextIdKeys The context ID keys.
|
|
8
9
|
* @param coreEngineConfig The core engine config.
|
|
9
10
|
* @param serverInfo The server information.
|
|
10
11
|
* @param openApiSpecPath The path to the open api spec.
|
|
11
12
|
* @param favIconPath The path to the favicon.
|
|
12
|
-
* @returns The
|
|
13
|
+
* @returns The config for the core and the server.
|
|
13
14
|
*/
|
|
14
|
-
export declare function buildEngineServerConfiguration(envVars: IEngineServerEnvironmentVariables,
|
|
15
|
+
export declare function buildEngineServerConfiguration(envVars: IEngineServerEnvironmentVariables, availableContextIdKeys: {
|
|
16
|
+
key: string;
|
|
17
|
+
requiredHandlerFeatures: string[];
|
|
18
|
+
}[], coreEngineConfig: IEngineCoreConfig, serverInfo: IServerInfo, openApiSpecPath?: string, favIconPath?: string): Promise<IEngineServerConfig>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { IEngineCore, IEngineServer } from "@twin.org/engine-models";
|
|
2
|
+
import type { INodeEngineConfig } from "../models/INodeEngineConfig.js";
|
|
3
|
+
import type { INodeEnvironmentVariables } from "../models/INodeEnvironmentVariables.js";
|
|
4
|
+
/**
|
|
5
|
+
* Handles the configuration of the extensions.
|
|
6
|
+
* @param envVars The environment variables for the node.
|
|
7
|
+
* @param nodeEngineConfig The node engine config.
|
|
8
|
+
* @returns The config for the core and the server.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extensionsConfiguration(envVars: INodeEnvironmentVariables, nodeEngineConfig: INodeEngineConfig): Promise<INodeEngineConfig>;
|
|
11
|
+
/**
|
|
12
|
+
* Handles the initialisation of the extensions when the engine has been constructed.
|
|
13
|
+
* @param envVars The environment variables for the node.
|
|
14
|
+
* @param engineCore The engine core instance.
|
|
15
|
+
* @returns Nothing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extensionsInitialiseEngine(envVars: INodeEnvironmentVariables, engineCore: IEngineCore): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Handles the initialisation of the extensions when the engine server has been constructed.
|
|
20
|
+
* @param envVars The environment variables for the node.
|
|
21
|
+
* @param engineCore The engine core instance.
|
|
22
|
+
* @param engineServer The engine server instance.
|
|
23
|
+
* @returns Nothing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extensionsInitialiseEngineServer(envVars: INodeEnvironmentVariables, engineCore: IEngineCore, engineServer: IEngineServer): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Handles the shutdown of the extensions.
|
|
28
|
+
* @param envVars The environment variables for the node.
|
|
29
|
+
* @param engineCore The engine core instance.
|
|
30
|
+
* @returns Nothing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function shutdownExtensions(envVars: INodeEnvironmentVariables, engineCore: IEngineCore): Promise<void>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { IEngineCore } from "@twin.org/engine-models";
|
|
2
|
+
import type { ICliArgs } from "./models/ICliArgs.js";
|
|
3
|
+
import type { ICliCommand } from "./models/ICliCommand.js";
|
|
4
|
+
import type { INodeEnvironmentVariables } from "./models/INodeEnvironmentVariables.js";
|
|
5
|
+
/**
|
|
6
|
+
* Initialise the CLI.
|
|
7
|
+
* @param processEnv The environment variables from the process.
|
|
8
|
+
* @param args The command line arguments.
|
|
9
|
+
* @returns The constructed CLI command if there is one.
|
|
10
|
+
* @throws GeneralError if the command is missing or invalid.
|
|
11
|
+
*/
|
|
12
|
+
export declare function initCli(processEnv: {
|
|
13
|
+
[id: string]: string;
|
|
14
|
+
}, args?: string[]): ICliCommand | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Parse command line arguments.
|
|
17
|
+
* @param args The command line arguments.
|
|
18
|
+
* @returns The parsed command line arguments.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseCommandLineArgs(args?: string[]): ICliArgs;
|
|
21
|
+
/**
|
|
22
|
+
* Construct the CLI command from the parsed arguments.
|
|
23
|
+
* @param processEnv The environment variables from the process.
|
|
24
|
+
* @param cliArgs The parsed CLI arguments.
|
|
25
|
+
* @returns The constructed CLI command.
|
|
26
|
+
* @throws GeneralError if the command is missing.
|
|
27
|
+
*/
|
|
28
|
+
export declare function constructCliCommand(processEnv: {
|
|
29
|
+
[id: string]: string;
|
|
30
|
+
}, cliArgs: ICliArgs): ICliCommand | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Execute the CLI command.
|
|
33
|
+
* @param engineCore The engine core.
|
|
34
|
+
* @param envVars The environment variables for the node.
|
|
35
|
+
* @param cliCommand The CLI command to execute.
|
|
36
|
+
*/
|
|
37
|
+
export declare function executeCommand(engineCore: IEngineCore, envVars: INodeEnvironmentVariables, cliCommand: ICliCommand): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Load the env files and process the options.
|
|
40
|
+
* @param processEnv The environment variables from the process.
|
|
41
|
+
* @param options The options.
|
|
42
|
+
* @returns The processed parameters.
|
|
43
|
+
* @throws GeneralError if an env file has errors.
|
|
44
|
+
*/
|
|
45
|
+
export declare function processEnvOptions(processEnv: {
|
|
46
|
+
[id: string]: string;
|
|
47
|
+
}, options: {
|
|
48
|
+
key: string;
|
|
49
|
+
value: string;
|
|
50
|
+
}[]): void;
|
|
51
|
+
/**
|
|
52
|
+
* Process options and replace any env variables with their values.
|
|
53
|
+
* @param processEnv The environment variables from the process.
|
|
54
|
+
* @param options The options.
|
|
55
|
+
* @throws GeneralError if an env variable is missing.
|
|
56
|
+
*/
|
|
57
|
+
export declare function substituteEnvOptions(processEnv: {
|
|
58
|
+
[id: string]: string;
|
|
59
|
+
}, options: {
|
|
60
|
+
key: string;
|
|
61
|
+
value: string;
|
|
62
|
+
}[]): void;
|
|
63
|
+
/**
|
|
64
|
+
* Register available CLI commands.
|
|
65
|
+
*/
|
|
66
|
+
export declare function registerCommands(): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { IEngineCore } from "@twin.org/engine-models";
|
|
2
|
+
import type { ICliCommandDefinition } from "../models/ICliCommandDefinition.js";
|
|
3
|
+
import type { INodeEngineConfig } from "../models/INodeEngineConfig.js";
|
|
4
|
+
import type { INodeEngineState } from "../models/INodeEngineState.js";
|
|
5
|
+
import type { INodeEnvironmentVariables } from "../models/INodeEnvironmentVariables.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get the command definition parameters.
|
|
8
|
+
* @param commandDefinitions The registered command definitions.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getCommandDefinitionBootstrapLegacy(commandDefinitions: {
|
|
11
|
+
[id: string]: ICliCommandDefinition;
|
|
12
|
+
}): void;
|
|
13
|
+
/**
|
|
14
|
+
* Command for legacy bootstrap.
|
|
15
|
+
* @param engineCore The engine core.
|
|
16
|
+
* @param envVars The environment variables for the node.
|
|
17
|
+
* @param params The parameters for the command.
|
|
18
|
+
*/
|
|
19
|
+
export declare function bootstrapLegacy(engineCore: IEngineCore<INodeEngineConfig, INodeEngineState>, envVars: INodeEnvironmentVariables & {
|
|
20
|
+
/**
|
|
21
|
+
* The features that are enabled on the node.
|
|
22
|
+
* @default []
|
|
23
|
+
*/
|
|
24
|
+
features?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The identity of the node which, if empty and node-identity feature is enabled it will be generated.
|
|
27
|
+
*/
|
|
28
|
+
nodeIdentity?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The mnemonic for the identity, if empty and node-identity feature is enabled it will be randomly generated.
|
|
31
|
+
*/
|
|
32
|
+
nodeMnemonic?: string;
|
|
33
|
+
/**
|
|
34
|
+
* A tenant id to use as a default for the node.
|
|
35
|
+
*/
|
|
36
|
+
tenantId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* A tenant api key to use as a default for the node.
|
|
39
|
+
*/
|
|
40
|
+
tenantApiKey?: string;
|
|
41
|
+
/**
|
|
42
|
+
* If the node-admin-user feature is enabled, this will be the organization of the user, if one is not provided it will be generated
|
|
43
|
+
*/
|
|
44
|
+
organizationIdentity?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The mnemonic for the organization, if empty and node-admin-user feature is enabled it will be randomly generated.
|
|
47
|
+
*/
|
|
48
|
+
organizationMnemonic?: string;
|
|
49
|
+
/**
|
|
50
|
+
* If the node-admin-user feature is enabled, this will be the identity of the user, if one is not provided it will be generated
|
|
51
|
+
*/
|
|
52
|
+
adminUserIdentity?: string;
|
|
53
|
+
/**
|
|
54
|
+
* The mnemonic for the admin user, if empty and node-admin-user feature is enabled it will be randomly generated.
|
|
55
|
+
*/
|
|
56
|
+
adminUserMnemonic?: string;
|
|
57
|
+
/**
|
|
58
|
+
* If the node-admin-user feature is enabled, this will be the name of the user.
|
|
59
|
+
* @default admin@node
|
|
60
|
+
*/
|
|
61
|
+
adminUserName?: string;
|
|
62
|
+
/**
|
|
63
|
+
* If the node-admin-user feature is enabled, this will be the password of the user, if empty it will be randomly generated.
|
|
64
|
+
*/
|
|
65
|
+
adminUserPassword?: string;
|
|
66
|
+
}, params: {}): Promise<void>;
|