@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/utils.js
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
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, Is } 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
|
+
import { NodeFeatures } from "./models/nodeFeatures.js";
|
|
13
|
+
/**
|
|
14
|
+
* Initialise the locales for the application.
|
|
15
|
+
* @param localesDirectory The directory containing the locales.
|
|
16
|
+
*/
|
|
17
|
+
export async function initialiseLocales(localesDirectory) {
|
|
18
|
+
const localesFile = path.resolve(path.join(localesDirectory, "en.json"));
|
|
19
|
+
CLIDisplay.value("Locales File", localesFile);
|
|
20
|
+
if (await fileExists(localesFile)) {
|
|
21
|
+
const enLangContent = await readFile(localesFile, "utf8");
|
|
22
|
+
I18n.addDictionary("en", JSON.parse(enLangContent));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
CLIDisplay.error(`Locales file not found: ${localesFile}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the directory where the application is being executed.
|
|
30
|
+
* @returns The execution directory.
|
|
31
|
+
*/
|
|
32
|
+
export function getExecutionDirectory() {
|
|
33
|
+
return process.cwd();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Does the specified file exist.
|
|
37
|
+
* @param filename The filename to check for existence.
|
|
38
|
+
* @returns True if the file exists.
|
|
39
|
+
*/
|
|
40
|
+
export async function fileExists(filename) {
|
|
41
|
+
try {
|
|
42
|
+
const stats = await stat(filename);
|
|
43
|
+
return stats.isFile();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Does the specified directory exist.
|
|
51
|
+
* @param directory The directory to check for existence.
|
|
52
|
+
* @returns True if the directory exists.
|
|
53
|
+
*/
|
|
54
|
+
export async function directoryExists(directory) {
|
|
55
|
+
try {
|
|
56
|
+
const stats = await stat(directory);
|
|
57
|
+
return stats.isDirectory();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the sub folders for the folder.
|
|
65
|
+
* @param directory The directory to get the sub folders.
|
|
66
|
+
* @returns The list of sub folders.
|
|
67
|
+
*/
|
|
68
|
+
export async function getSubFolders(directory) {
|
|
69
|
+
try {
|
|
70
|
+
const dir = await readdir(directory);
|
|
71
|
+
const folders = [];
|
|
72
|
+
for (const dirEntry of dir) {
|
|
73
|
+
const fullPath = path.join(directory, dirEntry);
|
|
74
|
+
const stats = await stat(fullPath);
|
|
75
|
+
if (stats.isDirectory()) {
|
|
76
|
+
folders.push(fullPath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return folders;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the files in the directory.
|
|
87
|
+
* @param directory The directory to get the files from.
|
|
88
|
+
* @returns The list of files in the directory.
|
|
89
|
+
*/
|
|
90
|
+
export async function getFiles(directory) {
|
|
91
|
+
try {
|
|
92
|
+
const dir = await readdir(directory);
|
|
93
|
+
const files = [];
|
|
94
|
+
for (const dirEntry of dir) {
|
|
95
|
+
const fullPath = path.join(directory, dirEntry);
|
|
96
|
+
const stats = await stat(fullPath);
|
|
97
|
+
if (stats.isFile()) {
|
|
98
|
+
files.push(fullPath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return files;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Load the text file.
|
|
109
|
+
* @param filename The filename of the text file to load.
|
|
110
|
+
* @returns The contents of the text file if it could not be loaded.
|
|
111
|
+
*/
|
|
112
|
+
export async function loadTextFile(filename) {
|
|
113
|
+
return readFile(filename, "utf8");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load the JSON file.
|
|
117
|
+
* @param filename The filename of the JSON file to load.
|
|
118
|
+
* @returns The contents of the JSON file or null if it could not be loaded.
|
|
119
|
+
*/
|
|
120
|
+
export async function loadJsonFile(filename) {
|
|
121
|
+
const content = await loadTextFile(filename);
|
|
122
|
+
return JSON.parse(content);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get the features that are enabled on the node.
|
|
126
|
+
* @param env The environment variables for the node.
|
|
127
|
+
* @returns The features that are enabled on the node.
|
|
128
|
+
*/
|
|
129
|
+
export function getFeatures(env) {
|
|
130
|
+
if (Is.empty(env.features)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const features = [];
|
|
134
|
+
const allFeatures = Object.values(NodeFeatures);
|
|
135
|
+
const splitFeatures = env.features.split(",");
|
|
136
|
+
for (const feature of splitFeatures) {
|
|
137
|
+
const featureTrimmed = feature.trim();
|
|
138
|
+
if (allFeatures.includes(featureTrimmed)) {
|
|
139
|
+
features.push(featureTrimmed);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return features;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Parse the protocol from a module name.
|
|
146
|
+
* @param moduleName The module name to parse.
|
|
147
|
+
* @returns The parsed protocol information.
|
|
148
|
+
*/
|
|
149
|
+
export function parseModuleProtocol(moduleName) {
|
|
150
|
+
const trimmed = moduleName.trim();
|
|
151
|
+
if (trimmed.startsWith("npm:")) {
|
|
152
|
+
return {
|
|
153
|
+
protocol: ModuleProtocol.Npm,
|
|
154
|
+
identifier: trimmed.slice(4),
|
|
155
|
+
original: trimmed
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (trimmed.startsWith("https://")) {
|
|
159
|
+
return {
|
|
160
|
+
protocol: ModuleProtocol.Https,
|
|
161
|
+
identifier: trimmed,
|
|
162
|
+
original: trimmed
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (trimmed.startsWith("http://")) {
|
|
166
|
+
return {
|
|
167
|
+
protocol: ModuleProtocol.Http,
|
|
168
|
+
identifier: trimmed,
|
|
169
|
+
original: trimmed
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (trimmed.startsWith("file://")) {
|
|
173
|
+
return {
|
|
174
|
+
protocol: ModuleProtocol.Local,
|
|
175
|
+
identifier: trimmed,
|
|
176
|
+
original: trimmed
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (ModuleHelper.isLocalModule(trimmed)) {
|
|
180
|
+
return {
|
|
181
|
+
protocol: ModuleProtocol.Local,
|
|
182
|
+
identifier: trimmed,
|
|
183
|
+
original: trimmed
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
protocol: ModuleProtocol.Default,
|
|
188
|
+
identifier: trimmed,
|
|
189
|
+
original: trimmed
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Hash a URL to create a safe filename.
|
|
194
|
+
* @param url The URL to hash.
|
|
195
|
+
* @returns A hashed filename safe for the filesystem.
|
|
196
|
+
*/
|
|
197
|
+
export function hashUrl(url) {
|
|
198
|
+
const urlBytes = Converter.utf8ToBytes(url);
|
|
199
|
+
const hashBytes = Sha256.sum256(urlBytes);
|
|
200
|
+
const hash = Converter.bytesToHex(hashBytes);
|
|
201
|
+
const ext = path.extname(new URL(url).pathname);
|
|
202
|
+
return `${hash}${ext}`;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get the extensions cache directory.
|
|
206
|
+
* @param executionDirectory The execution directory.
|
|
207
|
+
* @param protocol The protocol type for subdirectory organization.
|
|
208
|
+
* @param cacheDirectory The cache directory base path.
|
|
209
|
+
* @returns The cache directory path.
|
|
210
|
+
*/
|
|
211
|
+
export function getExtensionsCacheDir(executionDirectory, protocol, cacheDirectory) {
|
|
212
|
+
// Resolve to absolute path to ensure consistent behavior
|
|
213
|
+
const absoluteDir = path.resolve(executionDirectory);
|
|
214
|
+
const baseDir = cacheDirectory ?? ".tmp";
|
|
215
|
+
return path.join(absoluteDir, baseDir, "extensions", protocol);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Handle the npm: protocol by installing the package if needed.
|
|
219
|
+
* @param packageName The npm package name (without npm: prefix).
|
|
220
|
+
* @param executionDirectory The execution directory.
|
|
221
|
+
* @param cacheDirectory The cache directory base path.
|
|
222
|
+
* @returns The resolved path to the installed module.
|
|
223
|
+
*/
|
|
224
|
+
export async function handleNpmProtocol(packageName, executionDirectory, cacheDirectory) {
|
|
225
|
+
const cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory);
|
|
226
|
+
// Extract just the package name (without version) for the directory
|
|
227
|
+
// e.g. "picocolors@1.0.0" becomes "picocolors"
|
|
228
|
+
// e.g. "@scope/package@1.0.0" becomes "@scope/package"
|
|
229
|
+
const lastAtIndex = packageName.lastIndexOf("@");
|
|
230
|
+
const packageNameOnly = lastAtIndex > 0 ? packageName.slice(0, lastAtIndex) : packageName;
|
|
231
|
+
const packageDir = path.join(cacheDir, "node_modules", packageNameOnly);
|
|
232
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
233
|
+
const exists = await fileExists(packageJsonPath);
|
|
234
|
+
if (exists) {
|
|
235
|
+
const mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);
|
|
236
|
+
const modulePath = path.join(packageDir, mainFile);
|
|
237
|
+
return {
|
|
238
|
+
resolvedPath: modulePath,
|
|
239
|
+
cached: true
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
await mkdir(cacheDir, { recursive: true });
|
|
243
|
+
CLIDisplay.task(I18n.formatMessage("node.extensionNpmInstalling"), packageName);
|
|
244
|
+
try {
|
|
245
|
+
// Always pipe stdio to comply with env access restrictions in tests/lint
|
|
246
|
+
const stdio = "pipe";
|
|
247
|
+
execSync(`npm install ${packageName} --prefix "${cacheDir}" --no-save --no-package-lock`, {
|
|
248
|
+
cwd: cacheDir,
|
|
249
|
+
stdio
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
throw new GeneralError("node", "extensionNpmInstallFailed", {
|
|
254
|
+
package: packageName
|
|
255
|
+
}, BaseError.fromError(err));
|
|
256
|
+
}
|
|
257
|
+
const mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);
|
|
258
|
+
const modulePath = path.join(packageDir, mainFile);
|
|
259
|
+
return {
|
|
260
|
+
resolvedPath: modulePath,
|
|
261
|
+
cached: false
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Check if a cached file has expired based on TTL and force refresh settings.
|
|
266
|
+
* @param metadataPath Path to the cache metadata file.
|
|
267
|
+
* @param ttlHours Time to live in hours.
|
|
268
|
+
* @param forceRefresh Whether to force refresh regardless of TTL.
|
|
269
|
+
* @returns True if the cache is expired or should be refreshed.
|
|
270
|
+
*/
|
|
271
|
+
export async function isCacheExpired(metadataPath, ttlHours, forceRefresh) {
|
|
272
|
+
if (forceRefresh) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
const metadata = await loadJsonFile(metadataPath);
|
|
277
|
+
const ttlMillis = ttlHours * 60 * 60 * 1000;
|
|
278
|
+
const expireTime = metadata.downloadedAt + ttlMillis;
|
|
279
|
+
return Date.now() > expireTime;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
// If metadata doesn't exist or is corrupted, consider expired
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Handle the https: protocol by downloading the module if needed.
|
|
288
|
+
* @param url The HTTPS URL to download from.
|
|
289
|
+
* @param executionDirectory The execution directory.
|
|
290
|
+
* @param maxSizeMb The maximum size in MB for the download.
|
|
291
|
+
* @param cacheDirectory The cache directory base path.
|
|
292
|
+
* @param ttlHours TTL in hours for cache expiration.
|
|
293
|
+
* @param forceRefresh Whether to force refresh the cache.
|
|
294
|
+
* @returns The resolved path to the downloaded module.
|
|
295
|
+
*/
|
|
296
|
+
export async function handleHttpsProtocol(url, executionDirectory, maxSizeMb, cacheDirectory, ttlHours, forceRefresh) {
|
|
297
|
+
const effectiveTtlHours = ttlHours ?? 24;
|
|
298
|
+
const effectiveForceRefresh = forceRefresh ?? false;
|
|
299
|
+
const cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Https, cacheDirectory);
|
|
300
|
+
const filename = hashUrl(url);
|
|
301
|
+
const cachedPath = path.join(cacheDir, filename);
|
|
302
|
+
const metadataPath = `${cachedPath}.meta`;
|
|
303
|
+
const exists = await fileExists(cachedPath);
|
|
304
|
+
if (exists) {
|
|
305
|
+
const expired = await isCacheExpired(metadataPath, effectiveTtlHours, effectiveForceRefresh);
|
|
306
|
+
if (!expired) {
|
|
307
|
+
return {
|
|
308
|
+
resolvedPath: cachedPath,
|
|
309
|
+
cached: true
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
if (effectiveForceRefresh) {
|
|
313
|
+
CLIDisplay.warning(I18n.formatMessage("node.extensionForceRefresh", { url }));
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
CLIDisplay.task(I18n.formatMessage("node.extensionCacheExpired", { url }));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
CLIDisplay.warning(I18n.formatMessage("node.extensionSecurityWarning", { url }));
|
|
320
|
+
CLIDisplay.task(I18n.formatMessage("node.extensionHttpsDownloading"), url);
|
|
321
|
+
await mkdir(cacheDir, { recursive: true });
|
|
322
|
+
const maxSizeBytes = maxSizeMb * 1024 * 1024;
|
|
323
|
+
let downloadedSize = 0;
|
|
324
|
+
const chunks = [];
|
|
325
|
+
try {
|
|
326
|
+
await new Promise((resolve, reject) => {
|
|
327
|
+
httpsGet(url, response => {
|
|
328
|
+
if (response.statusCode !== 200) {
|
|
329
|
+
reject(new GeneralError("node", "extensionDownloadFailed", {
|
|
330
|
+
url,
|
|
331
|
+
status: response.statusCode ?? 0
|
|
332
|
+
}));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
response.on("data", (chunk) => {
|
|
336
|
+
downloadedSize += chunk.length;
|
|
337
|
+
if (downloadedSize > maxSizeBytes) {
|
|
338
|
+
response.destroy();
|
|
339
|
+
reject(new GeneralError("node", "extensionSizeLimitExceeded", {
|
|
340
|
+
size: downloadedSize,
|
|
341
|
+
limit: maxSizeBytes
|
|
342
|
+
}));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
chunks.push(chunk);
|
|
346
|
+
});
|
|
347
|
+
response.on("end", () => {
|
|
348
|
+
resolve();
|
|
349
|
+
});
|
|
350
|
+
response.on("error", err => {
|
|
351
|
+
reject(BaseError.fromError(err));
|
|
352
|
+
});
|
|
353
|
+
}).on("error", err => {
|
|
354
|
+
reject(BaseError.fromError(err));
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
throw new GeneralError("node", "extensionDownloadFailed", {
|
|
360
|
+
url
|
|
361
|
+
}, BaseError.fromError(err));
|
|
362
|
+
}
|
|
363
|
+
const tempPath = `${cachedPath}.tmp`;
|
|
364
|
+
await writeFile(tempPath, Buffer.concat(chunks));
|
|
365
|
+
// Atomic move from temp to final location
|
|
366
|
+
await rename(tempPath, cachedPath);
|
|
367
|
+
// Save metadata for TTL tracking
|
|
368
|
+
const metadata = {
|
|
369
|
+
downloadedAt: Date.now(),
|
|
370
|
+
url,
|
|
371
|
+
size: Buffer.concat(chunks).length
|
|
372
|
+
};
|
|
373
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
374
|
+
return {
|
|
375
|
+
resolvedPath: cachedPath,
|
|
376
|
+
cached: false
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Resolve the main entry point from a package directory using Node.js resolution with fallback.
|
|
381
|
+
* Uses require.resolve() when possible for standard Node.js behavior, with manual fallback.
|
|
382
|
+
* @param packagePath The absolute path to the package directory.
|
|
383
|
+
* @param packageName The package name for require.resolve().
|
|
384
|
+
* @param fallback The fallback file name if no entry point is found.
|
|
385
|
+
* @returns The resolved entry point file name (relative to package directory).
|
|
386
|
+
*/
|
|
387
|
+
export async function resolvePackageEntryPoint(packagePath, packageName, fallback = "index.js") {
|
|
388
|
+
try {
|
|
389
|
+
// Try require.resolve() first - handles exports, main, module automatically
|
|
390
|
+
const resolvedPath = require.resolve(packageName, { paths: [path.dirname(packagePath)] });
|
|
391
|
+
// Convert absolute path back to relative filename within package
|
|
392
|
+
const relativePath = path.relative(packagePath, resolvedPath);
|
|
393
|
+
return relativePath ?? fallback;
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// Fallback to manual package.json parsing if require.resolve fails
|
|
397
|
+
try {
|
|
398
|
+
const packageJsonPath = path.join(packagePath, "package.json");
|
|
399
|
+
const packageJsonContent = await loadJsonFile(packageJsonPath);
|
|
400
|
+
// Future: Could expand exports field support here
|
|
401
|
+
return packageJsonContent.module ?? packageJsonContent.main ?? fallback;
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return fallback;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Convert a file path to an import-compatible URL for cross-platform module loading.
|
|
410
|
+
* On Windows, adds the 'file://' protocol prefix required for dynamic imports.
|
|
411
|
+
* On other platforms, returns the path unchanged.
|
|
412
|
+
* @param filePath The absolute file path to convert.
|
|
413
|
+
* @returns A URL string compatible with dynamic import().
|
|
414
|
+
*/
|
|
415
|
+
export function createModuleImportUrl(filePath) {
|
|
416
|
+
return process.platform === "win32" ? `file://${filePath}` : filePath;
|
|
417
|
+
}
|
|
418
|
+
//# 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 {\n\tBaseError,\n\tConverter,\n\tGeneralError,\n\tI18n,\n\tIs,\n\ttype ILocaleDictionary\n} 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 { INodeEnvironmentVariables } from \"./models/INodeEnvironmentVariables.js\";\nimport type { IProtocolHandlerResult } from \"./models/IProtocolHandlerResult.js\";\nimport { ModuleProtocol } from \"./models/moduleProtocol.js\";\nimport { NodeFeatures } from \"./models/nodeFeatures.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 * Get the features that are enabled on the node.\n * @param env The environment variables for the node.\n * @returns The features that are enabled on the node.\n */\nexport function getFeatures(env: INodeEnvironmentVariables): NodeFeatures[] {\n\tif (Is.empty(env.features)) {\n\t\treturn [];\n\t}\n\n\tconst features: NodeFeatures[] = [];\n\tconst allFeatures = Object.values(NodeFeatures);\n\n\tconst splitFeatures = env.features.split(\",\");\n\tfor (const feature of splitFeatures) {\n\t\tconst featureTrimmed = feature.trim() as NodeFeatures;\n\t\tif (allFeatures.includes(featureTrimmed)) {\n\t\t\tfeatures.push(featureTrimmed);\n\t\t}\n\t}\n\n\treturn features;\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,14 +1,31 @@
|
|
|
1
|
-
import type { IEngineCore, IEngineCoreContext
|
|
1
|
+
import type { IEngineCore, IEngineCoreContext } from "@twin.org/engine-models";
|
|
2
2
|
import { type IEngineServerConfig } from "@twin.org/engine-server-types";
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type { INodeEngineState } from "./models/INodeEngineState.js";
|
|
4
|
+
import type { INodeEnvironmentVariables } from "./models/INodeEnvironmentVariables.js";
|
|
5
|
+
import { NodeFeatures } from "./models/nodeFeatures.js";
|
|
5
6
|
/**
|
|
6
7
|
* Bootstrap the application.
|
|
7
8
|
* @param engineCore The engine core for the node.
|
|
8
9
|
* @param context The context for the node.
|
|
9
10
|
* @param envVars The environment variables for the node.
|
|
10
11
|
*/
|
|
11
|
-
export declare function bootstrap(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig,
|
|
12
|
+
export declare function bootstrap(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Bootstrap the context id handlers creating any necessary resources.
|
|
15
|
+
* @param engineCore The engine core for the node.
|
|
16
|
+
* @param context The context for the node.
|
|
17
|
+
* @param envVars The environment variables for the node.
|
|
18
|
+
* @param features The features that are enabled on the node. The features that are enabled on the node.
|
|
19
|
+
*/
|
|
20
|
+
export declare function bootstrapContextIdHandlers(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Bootstrap the node creating any necessary resources.
|
|
23
|
+
* @param engineCore The engine core for the node.
|
|
24
|
+
* @param context The context for the node.
|
|
25
|
+
* @param envVars The environment variables for the node.
|
|
26
|
+
* @param features The features that are enabled on the node. The features that are enabled on the node.
|
|
27
|
+
*/
|
|
28
|
+
export declare function bootstrapNodeId(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
12
29
|
/**
|
|
13
30
|
* Bootstrap the node creating any necessary resources.
|
|
14
31
|
* @param engineCore The engine core for the node.
|
|
@@ -16,7 +33,7 @@ export declare function bootstrap(engineCore: IEngineCore, context: IEngineCoreC
|
|
|
16
33
|
* @param envVars The environment variables for the node.
|
|
17
34
|
* @param features The features that are enabled on the node. The features that are enabled on the node.
|
|
18
35
|
*/
|
|
19
|
-
export declare function
|
|
36
|
+
export declare function bootstrapTenantId(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
20
37
|
/**
|
|
21
38
|
* Bootstrap the user.
|
|
22
39
|
* @param engineCore The engine core for the node.
|
|
@@ -24,7 +41,7 @@ export declare function bootstrapNodeIdentity(engineCore: IEngineCore, context:
|
|
|
24
41
|
* @param envVars The environment variables for the node.
|
|
25
42
|
* @param features The features that are enabled on the node.
|
|
26
43
|
*/
|
|
27
|
-
export declare function
|
|
44
|
+
export declare function bootstrapNodeAdminUser(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
28
45
|
/**
|
|
29
46
|
* Bootstrap the immutable proof verification methods.
|
|
30
47
|
* @param engineCore The engine core for the node.
|
|
@@ -32,7 +49,7 @@ export declare function bootstrapNodeUser(engineCore: IEngineCore, context: IEng
|
|
|
32
49
|
* @param envVars The environment variables for the node.
|
|
33
50
|
* @param features The features that are enabled on the node.
|
|
34
51
|
*/
|
|
35
|
-
export declare function bootstrapImmutableProofMethod(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig,
|
|
52
|
+
export declare function bootstrapImmutableProofMethod(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
36
53
|
/**
|
|
37
54
|
* Bootstrap the keys for blob encryption.
|
|
38
55
|
* @param engineCore The engine core for the node.
|
|
@@ -40,7 +57,7 @@ export declare function bootstrapImmutableProofMethod(engineCore: IEngineCore, c
|
|
|
40
57
|
* @param envVars The environment variables for the node.
|
|
41
58
|
* @param features The features that are enabled on the node.
|
|
42
59
|
*/
|
|
43
|
-
export declare function bootstrapBlobEncryption(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig,
|
|
60
|
+
export declare function bootstrapBlobEncryption(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
44
61
|
/**
|
|
45
62
|
* Bootstrap the JWT signing key.
|
|
46
63
|
* @param engineCore The engine core for the node.
|
|
@@ -48,7 +65,7 @@ export declare function bootstrapBlobEncryption(engineCore: IEngineCore, context
|
|
|
48
65
|
* @param envVars The environment variables for the node.
|
|
49
66
|
* @param features The features that are enabled on the node.
|
|
50
67
|
*/
|
|
51
|
-
export declare function bootstrapAuth(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig,
|
|
68
|
+
export declare function bootstrapAuth(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
52
69
|
/**
|
|
53
70
|
* Bootstrap the synchronised storage blob encryption and verification methods.
|
|
54
71
|
* @param engineCore The engine core for the node.
|
|
@@ -56,4 +73,4 @@ export declare function bootstrapAuth(engineCore: IEngineCore, context: IEngineC
|
|
|
56
73
|
* @param envVars The environment variables for the node.
|
|
57
74
|
* @param features The features that are enabled on the node.
|
|
58
75
|
*/
|
|
59
|
-
export declare function bootstrapSynchronisedStorage(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig,
|
|
76
|
+
export declare function bootstrapSynchronisedStorage(engineCore: IEngineCore, context: IEngineCoreContext<IEngineServerConfig, INodeEngineState>, envVars: INodeEnvironmentVariables, features: NodeFeatures[]): Promise<void>;
|
|
@@ -1,8 +1,9 @@
|
|
|
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): IEngineConfig
|
|
9
|
+
export declare function buildEngineConfiguration(envVars: IEngineEnvironmentVariables, contextIdKeys: string[]): Promise<IEngineConfig>;
|
|
@@ -1,14 +1,15 @@
|
|
|
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 contextIdKeys 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, coreEngineConfig: IEngineCoreConfig, serverInfo: IServerInfo, openApiSpecPath?: string, favIconPath?: string): IEngineServerConfig
|
|
15
|
+
export declare function buildEngineServerConfiguration(envVars: IEngineServerEnvironmentVariables, contextIdKeys: string[], 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,6 @@
|
|
|
1
|
+
export declare const ATTESTATION_VERIFICATION_METHOD_ID = "attestation-assertion";
|
|
2
|
+
export declare const IMMUTABLE_PROOF_VERIFICATION_METHOD_ID = "immutable-proof-assertion";
|
|
3
|
+
export declare const BLOB_STORAGE_ENCRYPTION_KEY_ID = "blob-encryption";
|
|
4
|
+
export declare const SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID = "synchronised-storage-blob-encryption";
|
|
5
|
+
export declare const VC_AUTHENTICATION_VERIFICATION_METHOD_ID = "node-authentication-assertion";
|
|
6
|
+
export declare const AUTH_SIGNING_KEY_ID = "auth-signing";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IEngineCore } from "@twin.org/engine-models";
|
|
2
|
+
import type { INodeEnvironmentVariables } from "./models/INodeEnvironmentVariables.js";
|
|
3
|
+
/**
|
|
4
|
+
* Generate an identity and fund it.
|
|
5
|
+
* @param engineCore The engine core for the node.
|
|
6
|
+
* @param envVars The environment variables for the node.
|
|
7
|
+
* @param configIdentity A identity from config to use.
|
|
8
|
+
* @param configMnemonic A mnemonic from config to use.
|
|
9
|
+
* @param controller The controller for the identity.
|
|
10
|
+
* @param identityType The type of identity.
|
|
11
|
+
* @param addWallet Whether to add a wallet for the identity.
|
|
12
|
+
* @returns The identity that was generated.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createIdentity(engineCore: IEngineCore, envVars: INodeEnvironmentVariables, configIdentity: string | undefined, configMnemonic: string | undefined, controller: string | undefined, identityType: "node" | "organization" | "user", addWallet: boolean): Promise<string>;
|