@twin.org/node-core 0.0.2-next.25 → 0.0.2-next.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +21 -7
  2. package/dist/cjs/index.cjs +387 -49
  3. package/dist/esm/index.mjs +382 -53
  4. package/dist/types/index.d.ts +4 -0
  5. package/dist/types/models/ICacheMetadata.d.ts +17 -0
  6. package/dist/types/models/IModuleProtocol.d.ts +18 -0
  7. package/dist/types/models/INodeEnvironmentVariables.d.ts +25 -0
  8. package/dist/types/models/IProtocolHandlerResult.d.ts +13 -0
  9. package/dist/types/models/moduleProtocol.d.ts +29 -0
  10. package/dist/types/node.d.ts +3 -2
  11. package/dist/types/utils.d.ts +67 -0
  12. package/docs/changelog.md +7 -0
  13. package/docs/detailed-guide.md +129 -0
  14. package/docs/reference/functions/createModuleImportUrl.md +21 -0
  15. package/docs/reference/functions/getExtensionsCacheDir.md +31 -0
  16. package/docs/reference/functions/handleHttpsProtocol.md +49 -0
  17. package/docs/reference/functions/handleNpmProtocol.md +31 -0
  18. package/docs/reference/functions/hashUrl.md +19 -0
  19. package/docs/reference/functions/isCacheExpired.md +31 -0
  20. package/docs/reference/functions/overrideModuleImport.md +8 -2
  21. package/docs/reference/functions/parseModuleProtocol.md +19 -0
  22. package/docs/reference/functions/resolvePackageEntryPoint.md +32 -0
  23. package/docs/reference/index.md +13 -0
  24. package/docs/reference/interfaces/ICacheMetadata.md +27 -0
  25. package/docs/reference/interfaces/IModuleProtocol.md +27 -0
  26. package/docs/reference/interfaces/INodeEnvironmentVariables.md +70 -0
  27. package/docs/reference/interfaces/IProtocolHandlerResult.md +19 -0
  28. package/docs/reference/type-aliases/ModuleProtocol.md +5 -0
  29. package/docs/reference/variables/ModuleProtocol.md +37 -0
  30. package/locales/en.json +11 -2
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TWIN Node Core
2
2
 
3
- TWIN Node Core for serving APIs using the specified configuration
3
+ Core components for running TWIN nodes with dynamic extension loading and protocol-based module resolution.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,14 +8,28 @@ TWIN Node Core for serving APIs using the specified configuration
8
8
  npm install @twin.org/node-core
9
9
  ```
10
10
 
11
- ## Examples
11
+ ## Quick Start
12
12
 
13
- Usage of the APIs is shown in the examples [docs/examples.md](docs/examples.md)
13
+ ```javascript
14
+ import { start } from '@twin.org/node-core';
14
15
 
15
- ## Reference
16
+ // Start a TWIN node
17
+ await start({
18
+ extensions: ['./my-extension.mjs']
19
+ });
20
+ ```
21
+
22
+ ## Key Features
23
+
24
+ - **Protocol-based extension loading** - Load from local files, npm packages, or HTTPS URLs
25
+ - **Dynamic module resolution** - Automatic installation and caching
26
+ - **Extension lifecycle hooks** - Customize node behavior at different stages
27
+ - **Security controls** - Size limits, HTTPS-only, and cache TTL
28
+
29
+ ## Documentation
16
30
 
17
- Detailed reference documentation for the API can be found in [docs/reference/index.md](docs/reference/index.md)
31
+ For detailed documentation, configuration options, and examples, see [docs/detailed-guide.md](docs/detailed-guide.md).
18
32
 
19
- ## Changelog
33
+ ## License
20
34
 
21
- The changes between each version can be found in [docs/changelog.md](docs/changelog.md)
35
+ Apache-2.0
@@ -9,13 +9,14 @@ var entityStorageModels = require('@twin.org/entity-storage-models');
9
9
  var identityModels = require('@twin.org/identity-models');
10
10
  var vaultModels = require('@twin.org/vault-models');
11
11
  var walletModels = require('@twin.org/wallet-models');
12
+ var node_child_process = require('node:child_process');
12
13
  var promises = require('node:fs/promises');
14
+ var node_https = require('node:https');
13
15
  var path = require('node:path');
14
16
  var cliCore = require('@twin.org/cli-core');
17
+ var modules = require('@twin.org/modules');
15
18
  var rightsManagementRestClient = require('@twin.org/rights-management-rest-client');
16
19
  var engineServer = require('@twin.org/engine-server');
17
- var modules = require('@twin.org/modules');
18
- var node_child_process = require('node:child_process');
19
20
  var dotenv = require('dotenv');
20
21
  var engine = require('@twin.org/engine');
21
22
  var engineCore = require('@twin.org/engine-core');
@@ -70,6 +71,35 @@ const NodeFeatures = {
70
71
  NodeWallet: "node-wallet"
71
72
  };
72
73
 
74
+ // Copyright 2024 IOTA Stiftung.
75
+ // SPDX-License-Identifier: Apache-2.0.
76
+ /**
77
+ * The protocol types for modules.
78
+ */
79
+ // eslint-disable-next-line @typescript-eslint/naming-convention
80
+ const ModuleProtocol = {
81
+ /**
82
+ * Local module (starts with . or / or file://).
83
+ */
84
+ Local: "local",
85
+ /**
86
+ * NPM package (starts with npm:).
87
+ */
88
+ Npm: "npm",
89
+ /**
90
+ * HTTPS URL (starts with https://).
91
+ */
92
+ Https: "https",
93
+ /**
94
+ * HTTP URL (starts with http://).
95
+ */
96
+ Http: "http",
97
+ /**
98
+ * Default/standard module resolution.
99
+ */
100
+ Default: "default"
101
+ };
102
+
73
103
  // Copyright 2024 IOTA Stiftung.
74
104
  // SPDX-License-Identifier: Apache-2.0.
75
105
  /**
@@ -203,6 +233,280 @@ function getFeatures(env) {
203
233
  }
204
234
  return features;
205
235
  }
236
+ /**
237
+ * Parse the protocol from a module name.
238
+ * @param moduleName The module name to parse.
239
+ * @returns The parsed protocol information.
240
+ */
241
+ function parseModuleProtocol(moduleName) {
242
+ const trimmed = moduleName.trim();
243
+ if (trimmed.startsWith("npm:")) {
244
+ return {
245
+ protocol: ModuleProtocol.Npm,
246
+ identifier: trimmed.slice(4),
247
+ original: trimmed
248
+ };
249
+ }
250
+ if (trimmed.startsWith("https://")) {
251
+ return {
252
+ protocol: ModuleProtocol.Https,
253
+ identifier: trimmed,
254
+ original: trimmed
255
+ };
256
+ }
257
+ if (trimmed.startsWith("http://")) {
258
+ return {
259
+ protocol: ModuleProtocol.Http,
260
+ identifier: trimmed,
261
+ original: trimmed
262
+ };
263
+ }
264
+ if (trimmed.startsWith("file://")) {
265
+ return {
266
+ protocol: ModuleProtocol.Local,
267
+ identifier: trimmed,
268
+ original: trimmed
269
+ };
270
+ }
271
+ if (modules.ModuleHelper.isLocalModule(trimmed)) {
272
+ return {
273
+ protocol: ModuleProtocol.Local,
274
+ identifier: trimmed,
275
+ original: trimmed
276
+ };
277
+ }
278
+ return {
279
+ protocol: ModuleProtocol.Default,
280
+ identifier: trimmed,
281
+ original: trimmed
282
+ };
283
+ }
284
+ /**
285
+ * Hash a URL to create a safe filename.
286
+ * @param url The URL to hash.
287
+ * @returns A hashed filename safe for the filesystem.
288
+ */
289
+ function hashUrl(url) {
290
+ const urlBytes = core.Converter.utf8ToBytes(url);
291
+ const hashBytes = crypto.Sha256.sum256(urlBytes);
292
+ const hash = core.Converter.bytesToHex(hashBytes);
293
+ const ext = path.extname(new URL(url).pathname);
294
+ return `${hash}${ext}`;
295
+ }
296
+ /**
297
+ * Get the extensions cache directory.
298
+ * @param executionDirectory The execution directory.
299
+ * @param protocol The protocol type for subdirectory organization.
300
+ * @param cacheDirectory The cache directory base path.
301
+ * @returns The cache directory path.
302
+ */
303
+ function getExtensionsCacheDir(executionDirectory, protocol, cacheDirectory) {
304
+ // Resolve to absolute path to ensure consistent behavior
305
+ const absoluteDir = path.resolve(executionDirectory);
306
+ const baseDir = cacheDirectory ?? ".tmp";
307
+ return path.join(absoluteDir, baseDir, "extensions", protocol);
308
+ }
309
+ /**
310
+ * Handle the npm: protocol by installing the package if needed.
311
+ * @param packageName The npm package name (without npm: prefix).
312
+ * @param executionDirectory The execution directory.
313
+ * @param cacheDirectory The cache directory base path.
314
+ * @returns The resolved path to the installed module.
315
+ */
316
+ async function handleNpmProtocol(packageName, executionDirectory, cacheDirectory) {
317
+ const cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory);
318
+ // Extract just the package name (without version) for the directory
319
+ // e.g. "picocolors@1.0.0" becomes "picocolors"
320
+ // e.g. "@scope/package@1.0.0" becomes "@scope/package"
321
+ const lastAtIndex = packageName.lastIndexOf("@");
322
+ const packageNameOnly = lastAtIndex > 0 ? packageName.slice(0, lastAtIndex) : packageName;
323
+ const packageDir = path.join(cacheDir, "node_modules", packageNameOnly);
324
+ const packageJsonPath = path.join(packageDir, "package.json");
325
+ const exists = await fileExists(packageJsonPath);
326
+ if (exists) {
327
+ const mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);
328
+ const modulePath = path.join(packageDir, mainFile);
329
+ return {
330
+ resolvedPath: modulePath,
331
+ cached: true
332
+ };
333
+ }
334
+ await promises.mkdir(cacheDir, { recursive: true });
335
+ cliCore.CLIDisplay.task(core.I18n.formatMessage("node.extensionNpmInstalling"), packageName);
336
+ try {
337
+ // Always pipe stdio to comply with env access restrictions in tests/lint
338
+ const stdio = "pipe";
339
+ node_child_process.execSync(`npm install ${packageName} --prefix "${cacheDir}" --no-save --no-package-lock`, {
340
+ cwd: cacheDir,
341
+ stdio
342
+ });
343
+ }
344
+ catch (err) {
345
+ throw new core.GeneralError("node", "extensionNpmInstallFailed", {
346
+ package: packageName
347
+ }, core.BaseError.fromError(err));
348
+ }
349
+ const mainFile = await resolvePackageEntryPoint(packageDir, packageNameOnly);
350
+ const modulePath = path.join(packageDir, mainFile);
351
+ return {
352
+ resolvedPath: modulePath,
353
+ cached: false
354
+ };
355
+ }
356
+ /**
357
+ * Check if a cached file has expired based on TTL and force refresh settings.
358
+ * @param metadataPath Path to the cache metadata file.
359
+ * @param ttlHours Time to live in hours.
360
+ * @param forceRefresh Whether to force refresh regardless of TTL.
361
+ * @returns True if the cache is expired or should be refreshed.
362
+ */
363
+ async function isCacheExpired(metadataPath, ttlHours, forceRefresh) {
364
+ if (forceRefresh) {
365
+ return true;
366
+ }
367
+ try {
368
+ const metadata = await loadJsonFile(metadataPath);
369
+ const ttlMillis = ttlHours * 60 * 60 * 1000;
370
+ const expireTime = metadata.downloadedAt + ttlMillis;
371
+ return Date.now() > expireTime;
372
+ }
373
+ catch {
374
+ // If metadata doesn't exist or is corrupted, consider expired
375
+ return true;
376
+ }
377
+ }
378
+ /**
379
+ * Handle the https: protocol by downloading the module if needed.
380
+ * @param url The HTTPS URL to download from.
381
+ * @param executionDirectory The execution directory.
382
+ * @param maxSizeMb The maximum size in MB for the download.
383
+ * @param cacheDirectory The cache directory base path.
384
+ * @param ttlHours TTL in hours for cache expiration.
385
+ * @param forceRefresh Whether to force refresh the cache.
386
+ * @returns The resolved path to the downloaded module.
387
+ */
388
+ async function handleHttpsProtocol(url, executionDirectory, maxSizeMb, cacheDirectory, ttlHours, forceRefresh) {
389
+ const effectiveTtlHours = ttlHours ?? 24;
390
+ const effectiveForceRefresh = forceRefresh ?? false;
391
+ const cacheDir = getExtensionsCacheDir(executionDirectory, ModuleProtocol.Https, cacheDirectory);
392
+ const filename = hashUrl(url);
393
+ const cachedPath = path.join(cacheDir, filename);
394
+ const metadataPath = `${cachedPath}.meta`;
395
+ const exists = await fileExists(cachedPath);
396
+ if (exists) {
397
+ const expired = await isCacheExpired(metadataPath, effectiveTtlHours, effectiveForceRefresh);
398
+ if (!expired) {
399
+ return {
400
+ resolvedPath: cachedPath,
401
+ cached: true
402
+ };
403
+ }
404
+ if (effectiveForceRefresh) {
405
+ cliCore.CLIDisplay.warning(core.I18n.formatMessage("node.extensionForceRefresh", { url }));
406
+ }
407
+ else {
408
+ cliCore.CLIDisplay.task(core.I18n.formatMessage("node.extensionCacheExpired", { url }));
409
+ }
410
+ }
411
+ cliCore.CLIDisplay.warning(core.I18n.formatMessage("node.extensionSecurityWarning", { url }));
412
+ cliCore.CLIDisplay.task(core.I18n.formatMessage("node.extensionHttpsDownloading"), url);
413
+ await promises.mkdir(cacheDir, { recursive: true });
414
+ const maxSizeBytes = maxSizeMb * 1024 * 1024;
415
+ let downloadedSize = 0;
416
+ const chunks = [];
417
+ try {
418
+ await new Promise((resolve, reject) => {
419
+ node_https.get(url, response => {
420
+ if (response.statusCode !== 200) {
421
+ reject(new core.GeneralError("node", "extensionDownloadFailed", {
422
+ url,
423
+ status: response.statusCode ?? 0
424
+ }));
425
+ return;
426
+ }
427
+ response.on("data", (chunk) => {
428
+ downloadedSize += chunk.length;
429
+ if (downloadedSize > maxSizeBytes) {
430
+ response.destroy();
431
+ reject(new core.GeneralError("node", "extensionSizeLimitExceeded", {
432
+ size: downloadedSize,
433
+ limit: maxSizeBytes
434
+ }));
435
+ return;
436
+ }
437
+ chunks.push(chunk);
438
+ });
439
+ response.on("end", () => {
440
+ resolve();
441
+ });
442
+ response.on("error", err => {
443
+ reject(core.BaseError.fromError(err));
444
+ });
445
+ }).on("error", err => {
446
+ reject(core.BaseError.fromError(err));
447
+ });
448
+ });
449
+ }
450
+ catch (err) {
451
+ throw new core.GeneralError("node", "extensionDownloadFailed", {
452
+ url
453
+ }, core.BaseError.fromError(err));
454
+ }
455
+ const tempPath = `${cachedPath}.tmp`;
456
+ await promises.writeFile(tempPath, Buffer.concat(chunks));
457
+ // Atomic move from temp to final location
458
+ await promises.rename(tempPath, cachedPath);
459
+ // Save metadata for TTL tracking
460
+ const metadata = {
461
+ downloadedAt: Date.now(),
462
+ url,
463
+ size: Buffer.concat(chunks).length
464
+ };
465
+ await promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
466
+ return {
467
+ resolvedPath: cachedPath,
468
+ cached: false
469
+ };
470
+ }
471
+ /**
472
+ * Resolve the main entry point from a package directory using Node.js resolution with fallback.
473
+ * Uses require.resolve() when possible for standard Node.js behavior, with manual fallback.
474
+ * @param packagePath The absolute path to the package directory.
475
+ * @param packageName The package name for require.resolve().
476
+ * @param fallback The fallback file name if no entry point is found.
477
+ * @returns The resolved entry point file name (relative to package directory).
478
+ */
479
+ async function resolvePackageEntryPoint(packagePath, packageName, fallback = "index.js") {
480
+ try {
481
+ // Try require.resolve() first - handles exports, main, module automatically
482
+ const resolvedPath = require.resolve(packageName, { paths: [path.dirname(packagePath)] });
483
+ // Convert absolute path back to relative filename within package
484
+ const relativePath = path.relative(packagePath, resolvedPath);
485
+ return relativePath ?? fallback;
486
+ }
487
+ catch {
488
+ // Fallback to manual package.json parsing if require.resolve fails
489
+ try {
490
+ const packageJsonPath = path.join(packagePath, "package.json");
491
+ const packageJsonContent = await loadJsonFile(packageJsonPath);
492
+ // Future: Could expand exports field support here
493
+ return packageJsonContent.module ?? packageJsonContent.main ?? fallback;
494
+ }
495
+ catch {
496
+ return fallback;
497
+ }
498
+ }
499
+ }
500
+ /**
501
+ * Convert a file path to an import-compatible URL for cross-platform module loading.
502
+ * On Windows, adds the 'file://' protocol prefix required for dynamic imports.
503
+ * On other platforms, returns the path unchanged.
504
+ * @param filePath The absolute file path to convert.
505
+ * @returns A URL string compatible with dynamic import().
506
+ */
507
+ function createModuleImportUrl(filePath) {
508
+ return process.platform === "win32" ? `file://${filePath}` : filePath;
509
+ }
206
510
 
207
511
  // Copyright 2024 IOTA Stiftung.
208
512
  // SPDX-License-Identifier: Apache-2.0.
@@ -1994,7 +2298,7 @@ async function run(nodeOptions) {
1994
2298
  nodeOptions ??= {};
1995
2299
  const serverInfo = {
1996
2300
  name: nodeOptions?.serverName ?? "TWIN Node Server",
1997
- version: nodeOptions?.serverVersion ?? "0.0.2-next.25" // x-release-please-version
2301
+ version: nodeOptions?.serverVersion ?? "0.0.2-next.26" // x-release-please-version
1998
2302
  };
1999
2303
  cliCore.CLIDisplay.header(serverInfo.name, serverInfo.version, "🌩️ ");
2000
2304
  if (!core.Is.stringValue(nodeOptions?.executionDirectory)) {
@@ -2132,10 +2436,13 @@ async function buildConfiguration(processEnv, options, serverInfo) {
2132
2436
  return { nodeEngineConfig, nodeEnvVars: envVars };
2133
2437
  }
2134
2438
  /**
2135
- * Override module imports to use local files where possible.
2439
+ * Override module imports to support protocol-based loading (npm:, https:) and local files.
2136
2440
  * @param executionDirectory The execution directory for resolving local module paths.
2441
+ * @param envVars The environment variables containing extension configuration (optional, uses defaults if not provided).
2137
2442
  */
2138
- function overrideModuleImport(executionDirectory) {
2443
+ function overrideModuleImport(executionDirectory, envVars) {
2444
+ const maxSizeMb = core.Coerce.number(envVars?.extensionsMaxSizeMb) ?? 10;
2445
+ const cacheDirectory = envVars?.extensionsCacheDirectory;
2139
2446
  modules.ModuleHelper.overrideImport(async (moduleName) => {
2140
2447
  if (moduleCache[moduleName]) {
2141
2448
  return {
@@ -2143,55 +2450,77 @@ function overrideModuleImport(executionDirectory) {
2143
2450
  useDefault: false
2144
2451
  };
2145
2452
  }
2146
- // If the module path for example when dynamically loading
2147
- // modules looks like a local file then we try to resolve
2148
- // using the local file system
2149
- const isLocal = modules.ModuleHelper.isLocalModule(moduleName);
2150
- if (isLocal) {
2151
- // See if we can just resolve the filename locally
2152
- let localFilename = path.resolve(moduleName);
2153
- let exists = await fileExists(localFilename);
2154
- if (!exists) {
2155
- // Doesn't exist in the current directory, try the execution directory
2156
- localFilename = path.resolve(executionDirectory, moduleName);
2157
- exists = await fileExists(localFilename);
2158
- }
2159
- if (exists) {
2160
- // If the module exists then we can load it, otherwise
2161
- // we fallback to regular handling to see if that can import it
2162
- const module = await import(process.platform === "win32" ? `file://${localFilename}` : localFilename);
2163
- moduleCache[moduleName] = module;
2164
- return {
2165
- module,
2166
- useDefault: false
2167
- };
2453
+ const parsed = parseModuleProtocol(moduleName);
2454
+ let resolvedPath;
2455
+ switch (parsed.protocol) {
2456
+ case ModuleProtocol.Npm: {
2457
+ const result = await handleNpmProtocol(parsed.identifier, executionDirectory, cacheDirectory);
2458
+ resolvedPath = result.resolvedPath;
2459
+ break;
2168
2460
  }
2169
- }
2170
- try {
2171
- // Try and load from node_modules manually
2172
- // This is needed for some environments where
2173
- // the module resolution doesn't work as expected
2174
- const npmRoot = node_child_process.execSync("npm root").toString().trim().replace(/\\/g, "/");
2175
- const packageJson = await loadJsonFile(path.resolve(npmRoot, moduleName, "package.json"));
2176
- const mainFile = packageJson?.module ?? packageJson?.main ?? "index.js";
2177
- const modulePath = path.resolve(npmRoot, moduleName, mainFile);
2178
- const exists = await fileExists(modulePath);
2179
- if (exists) {
2180
- const module = await import(process.platform === "win32" ? `file://${modulePath}` : modulePath);
2181
- moduleCache[moduleName] = module;
2182
- return {
2183
- module,
2184
- useDefault: false
2185
- };
2461
+ case ModuleProtocol.Https: {
2462
+ const result = await handleHttpsProtocol(parsed.identifier, executionDirectory, maxSizeMb, cacheDirectory, envVars?.extensionsCacheTtlHours, envVars?.extensionsForceRefresh);
2463
+ resolvedPath = result.resolvedPath;
2464
+ break;
2465
+ }
2466
+ case ModuleProtocol.Http: {
2467
+ throw new core.GeneralError("node", "insecureProtocol", { protocol: ModuleProtocol.Http });
2468
+ }
2469
+ case ModuleProtocol.Local: {
2470
+ let localFilename = path.resolve(moduleName);
2471
+ let exists = await fileExists(localFilename);
2472
+ if (!exists) {
2473
+ localFilename = path.resolve(executionDirectory, moduleName);
2474
+ exists = await fileExists(localFilename);
2475
+ }
2476
+ if (exists) {
2477
+ resolvedPath = localFilename;
2478
+ }
2479
+ break;
2480
+ }
2481
+ case ModuleProtocol.Default: {
2482
+ try {
2483
+ const npmRoot = node_child_process.execSync("npm root").toString().trim().replace(/\\/g, "/");
2484
+ const packagePath = path.resolve(npmRoot, moduleName);
2485
+ const mainFile = await resolvePackageEntryPoint(packagePath, moduleName);
2486
+ const modulePath = path.resolve(packagePath, mainFile);
2487
+ const exists = await fileExists(modulePath);
2488
+ if (exists) {
2489
+ resolvedPath = modulePath;
2490
+ break;
2491
+ }
2492
+ }
2493
+ catch {
2494
+ // Continue to fallback resolution
2495
+ }
2496
+ // Fallback: resolve from npm protocol cache directory (installed via handleNpmProtocol)
2497
+ try {
2498
+ const cacheNpmRoot = path.resolve(getExtensionsCacheDir(executionDirectory, ModuleProtocol.Npm, cacheDirectory), "node_modules");
2499
+ const packagePath = path.resolve(cacheNpmRoot, moduleName);
2500
+ const mainFile = await resolvePackageEntryPoint(packagePath, moduleName);
2501
+ const modulePath = path.resolve(packagePath, mainFile);
2502
+ const exists = await fileExists(modulePath);
2503
+ if (exists) {
2504
+ resolvedPath = modulePath;
2505
+ }
2506
+ }
2507
+ catch {
2508
+ // No cached resolution either; fall through
2509
+ }
2510
+ break;
2186
2511
  }
2187
2512
  }
2188
- catch {
2189
- // We just fallback to default handling if not possible
2513
+ // Common module loading and caching logic
2514
+ if (resolvedPath) {
2515
+ const module = await import(createModuleImportUrl(resolvedPath));
2516
+ moduleCache[moduleName] = module;
2517
+ return {
2518
+ module,
2519
+ useDefault: false
2520
+ };
2190
2521
  }
2191
- // We don't appear to be able to manually resolve this module
2192
- // So we let the default handling take care of it
2193
- // This will allow built-in modules and regular node_modules to load as normal
2194
2522
  return {
2523
+ module: undefined,
2195
2524
  useDefault: true
2196
2525
  };
2197
2526
  });
@@ -2201,6 +2530,7 @@ exports.ATTESTATION_VERIFICATION_METHOD_ID = ATTESTATION_VERIFICATION_METHOD_ID;
2201
2530
  exports.AUTH_SIGNING_KEY_ID = AUTH_SIGNING_KEY_ID;
2202
2531
  exports.BLOB_STORAGE_ENCRYPTION_KEY_ID = BLOB_STORAGE_ENCRYPTION_KEY_ID;
2203
2532
  exports.IMMUTABLE_PROOF_VERIFICATION_METHOD_ID = IMMUTABLE_PROOF_VERIFICATION_METHOD_ID;
2533
+ exports.ModuleProtocol = ModuleProtocol;
2204
2534
  exports.NodeFeatures = NodeFeatures;
2205
2535
  exports.SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID = SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID;
2206
2536
  exports.VC_AUTHENTICATION_VERIFICATION_METHOD_ID = VC_AUTHENTICATION_VERIFICATION_METHOD_ID;
@@ -2214,19 +2544,27 @@ exports.bootstrapSynchronisedStorage = bootstrapSynchronisedStorage;
2214
2544
  exports.buildConfiguration = buildConfiguration;
2215
2545
  exports.buildEngineConfiguration = buildEngineConfiguration;
2216
2546
  exports.buildEngineServerConfiguration = buildEngineServerConfiguration;
2547
+ exports.createModuleImportUrl = createModuleImportUrl;
2217
2548
  exports.directoryExists = directoryExists;
2218
2549
  exports.extensionsConfiguration = extensionsConfiguration;
2219
2550
  exports.extensionsInitialiseEngine = extensionsInitialiseEngine;
2220
2551
  exports.extensionsInitialiseEngineServer = extensionsInitialiseEngineServer;
2221
2552
  exports.fileExists = fileExists;
2222
2553
  exports.getExecutionDirectory = getExecutionDirectory;
2554
+ exports.getExtensionsCacheDir = getExtensionsCacheDir;
2223
2555
  exports.getFeatures = getFeatures;
2224
2556
  exports.getFiles = getFiles;
2225
2557
  exports.getSubFolders = getSubFolders;
2558
+ exports.handleHttpsProtocol = handleHttpsProtocol;
2559
+ exports.handleNpmProtocol = handleNpmProtocol;
2560
+ exports.hashUrl = hashUrl;
2226
2561
  exports.initialiseLocales = initialiseLocales;
2562
+ exports.isCacheExpired = isCacheExpired;
2227
2563
  exports.loadJsonFile = loadJsonFile;
2228
2564
  exports.loadTextFile = loadTextFile;
2229
2565
  exports.overrideModuleImport = overrideModuleImport;
2566
+ exports.parseModuleProtocol = parseModuleProtocol;
2567
+ exports.resolvePackageEntryPoint = resolvePackageEntryPoint;
2230
2568
  exports.run = run;
2231
2569
  exports.shutdownExtensions = shutdownExtensions;
2232
2570
  exports.start = start;