@pipelab/core-node 1.0.0-beta.1 → 1.0.0-beta.11

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/dist/index.mjs CHANGED
@@ -2,13 +2,13 @@ import { createRequire } from "node:module";
2
2
  import path, { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
3
3
  import { homedir, platform, tmpdir } from "node:os";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
- import fs, { createWriteStream, existsSync } from "node:fs";
5
+ import fs, { createWriteStream, existsSync, readdirSync, statSync } from "node:fs";
6
6
  import { WebSocket, WebSocketServer as WebSocketServer$1 } from "ws";
7
7
  import http from "node:http";
8
8
  import { nanoid } from "nanoid";
9
9
  import { SubscriptionRequiredError, WebSocketError, appSettingsMigrator, configRegistry, fileRepoMigrations, isRequired, isSupabaseAvailable, isWebSocketRequestMessage, processGraph, savedFileMigrator, supabase, useLogger, usePlugins } from "@pipelab/shared";
10
10
  import { getUiDevServerMissingWarning, uiDevPort, websocketPort } from "@pipelab/constants";
11
- import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, unlink, writeFile } from "node:fs/promises";
11
+ import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
12
12
  import { execa } from "execa";
13
13
  import tar from "tar";
14
14
  import yauzl from "yauzl";
@@ -55,14 +55,16 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
55
55
  //#region src/context.ts
56
56
  const _dirname = typeof __dirname !== "undefined" ? __dirname : typeof import.meta !== "undefined" && import.meta.url ? dirname(fileURLToPath(import.meta.url)) : process.cwd();
57
57
  const isDev = process.env.NODE_ENV === "development";
58
- const getDefaultUserDataPath = () => {
59
- return join((() => {
58
+ const getDefaultUserDataPath = (env) => {
59
+ const base = (() => {
60
60
  switch (platform()) {
61
61
  case "win32": return process.env.APPDATA || join(homedir(), "AppData", "Roaming");
62
62
  case "darwin": return join(homedir(), "Library", "Application Support");
63
63
  default: return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
64
64
  }
65
- })(), "@pipelab", isDev ? "app-dev" : "app");
65
+ })();
66
+ const mode = env ?? (isDev ? "dev" : "prod");
67
+ return join(base, "@pipelab", mode === "dev" ? "app-dev" : mode === "beta" ? "app-beta" : "app");
66
68
  };
67
69
  /**
68
70
  * Finds the monorepo root by looking for pnpm-workspace.yaml.
@@ -78,8 +80,10 @@ function findProjectRoot(startDir) {
78
80
  const projectRoot = findProjectRoot(_dirname);
79
81
  var PipelabContext = class {
80
82
  userDataPath;
83
+ releaseTag;
81
84
  constructor(options) {
82
85
  this.userDataPath = options.userDataPath;
86
+ this.releaseTag = options.releaseTag || "latest";
83
87
  }
84
88
  getPackagesPath(...subpaths) {
85
89
  return join(this.userDataPath, "packages", ...subpaths);
@@ -628,25 +632,24 @@ const setupConfigFile = async (name, options) => {
628
632
  }
629
633
  let json = void 0;
630
634
  try {
631
- json = await migrator.migrate(originalJson, { debug: false });
635
+ json = await migrator.migrate(originalJson, {
636
+ debug: false,
637
+ onStep: async (state, version) => {
638
+ const parsedPath = path.parse(filesPath);
639
+ const versionedPath = path.join(parsedPath.dir, `${parsedPath.name}.v${version}.json`);
640
+ try {
641
+ await fs$1.writeFile(versionedPath, JSON.stringify(state));
642
+ logger().info(`Intermediate backup created for ${name} at ${versionedPath}`);
643
+ } catch (e) {
644
+ logger().error(`Failed to create intermediate backup for ${name} at v${version}:`, e);
645
+ }
646
+ }
647
+ });
632
648
  } catch (e) {
633
649
  logger().error(`Error migrating config ${name}:`, e);
634
650
  json = migrator.defaultValue;
635
651
  }
636
- const originalVersion = originalJson?.version;
637
- const newVersion = json?.version;
638
- if (originalVersion !== newVersion && content !== void 0) {
639
- const parsedPath = path.parse(filesPath);
640
- const versionSuffix = originalVersion || "unknown";
641
- const backupPath = path.join(parsedPath.dir, `${parsedPath.name}.${versionSuffix}.bak`);
642
- try {
643
- await fs$1.copyFile(filesPath, backupPath);
644
- logger().info(`Backup created for ${name} at ${backupPath} (version ${versionSuffix})`);
645
- } catch (e) {
646
- logger().error(`Failed to create backup for ${name}:`, e);
647
- }
648
- }
649
- if (originalVersion !== newVersion || content === void 0) try {
652
+ if (originalJson?.version !== json?.version || content === void 0) try {
650
653
  await fs$1.writeFile(filesPath, JSON.stringify(json));
651
654
  } catch (e) {
652
655
  logger().error(`Error saving migrated config ${name}:`, e);
@@ -8832,9 +8835,13 @@ const sendStartupReady = () => {
8832
8835
  };
8833
8836
  async function serveCommand(options, version, _dirname) {
8834
8837
  if (!options.userData) throw new Error("userDataPath is required for serveCommand");
8835
- const context = new PipelabContext({ userDataPath: options.userData });
8838
+ const releaseTag = version.includes("beta") ? "beta" : "latest";
8839
+ const context = new PipelabContext({
8840
+ userDataPath: options.userData,
8841
+ releaseTag
8842
+ });
8836
8843
  let rawAssetFolder;
8837
- if (!isDev) rawAssetFolder = await fetchPipelabAsset("@pipelab/ui", "latest", { context });
8844
+ if (!isDev) rawAssetFolder = await fetchPipelabAsset("@pipelab/ui", releaseTag, { context });
8838
8845
  const server = http$1.createServer(async (request, response) => {
8839
8846
  if (isDev) {
8840
8847
  response.writeHead(200, { "Content-Type": "text/html" });
@@ -8874,6 +8881,25 @@ async function serveCommand(options, version, _dirname) {
8874
8881
  //#region src/utils/remote.ts
8875
8882
  const DEFAULT_NODE_VERSION = "24.14.1";
8876
8883
  const DEFAULT_PNPM_VERSION = "10.12.0";
8884
+ function isPackageComplete(packageDir) {
8885
+ return existsSync(join(packageDir, "package.json"));
8886
+ }
8887
+ function isNodeJSComplete(nodePath) {
8888
+ try {
8889
+ return existsSync(nodePath) && statSync(nodePath).size > 0;
8890
+ } catch {
8891
+ return false;
8892
+ }
8893
+ }
8894
+ function isDependenciesInstalledSync(packageDir) {
8895
+ const nodeModulesPath = join(packageDir, "node_modules");
8896
+ if (!existsSync(nodeModulesPath)) return false;
8897
+ try {
8898
+ return readdirSync(nodeModulesPath).length > 0;
8899
+ } catch {
8900
+ return false;
8901
+ }
8902
+ }
8877
8903
  /**
8878
8904
  * In-memory lock to prevent concurrent operations on the same resource (e.g., downloading Node.js).
8879
8905
  */
@@ -8900,11 +8926,13 @@ async function withLock(key, fn) {
8900
8926
  * Centralized in core-node to avoid circular dependencies.
8901
8927
  */
8902
8928
  async function fetchPackage(packageName, versionOrRange, options) {
8929
+ const start = Date.now();
8903
8930
  if (isDev && projectRoot && process.env.PIPELAB_FORCE_NPM !== "true") {
8904
8931
  if (packageName.startsWith("@pipelab/")) {
8932
+ const localStart = Date.now();
8905
8933
  const local = await tryResolveMonorepoPackage(packageName);
8906
8934
  if (local) {
8907
- console.log(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir}`);
8935
+ console.log(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`);
8908
8936
  return {
8909
8937
  ...local,
8910
8938
  resolvedVersion: "workspace"
@@ -8916,6 +8944,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
8916
8944
  const baseDir = ctx.getPackagesPath(packageName);
8917
8945
  let resolvedVersion;
8918
8946
  console.log(`[Fetcher] Resolving ${packageName}@${versionOrRange || "latest"}...`);
8947
+ const resolveStart = Date.now();
8919
8948
  try {
8920
8949
  const cachePath = join(ctx.userDataPath, "cache", "pacote");
8921
8950
  let packumentPromise = packumentRequests.get(packageName);
@@ -8929,27 +8958,65 @@ async function fetchPackage(packageName, versionOrRange, options) {
8929
8958
  const foundVersion = packument["dist-tags"]?.[range] || semver.maxSatisfying(versions, range);
8930
8959
  if (!foundVersion) throw new Error(`Package ${packageName}@${range} not found on npm (available tags: ${Object.keys(packument["dist-tags"] || {}).join(", ")})`);
8931
8960
  resolvedVersion = foundVersion;
8932
- console.log(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm`);
8961
+ console.log(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`);
8933
8962
  } catch (error) {
8934
- console.warn(`[Fetcher] ${packageName}: remote resolution failed, trying local fallback...`);
8963
+ console.warn(`[Fetcher] ${packageName}: remote resolution failed (${Date.now() - resolveStart}ms), trying local fallback...`);
8964
+ const fallbackStart = Date.now();
8935
8965
  const fallbackVersion = await tryLocalFallback(versionOrRange, error, baseDir, packageName);
8936
- if (fallbackVersion) resolvedVersion = fallbackVersion;
8937
- else throw error;
8966
+ if (fallbackVersion) {
8967
+ resolvedVersion = fallbackVersion;
8968
+ console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
8969
+ } else throw error;
8938
8970
  }
8939
8971
  const cachePath = join(ctx.userDataPath, "cache", "pacote");
8940
8972
  const packageDir = join(baseDir, resolvedVersion);
8941
- if (existsSync(packageDir) && !options?.installDeps) return {
8942
- packageDir,
8943
- resolvedVersion
8944
- };
8973
+ const checkStart = Date.now();
8974
+ const isInstalled = options?.installDeps ? isPackageComplete(packageDir) && isDependenciesInstalledSync(packageDir) : isPackageComplete(packageDir);
8975
+ const checkDuration = Date.now() - checkStart;
8976
+ if (isInstalled) {
8977
+ console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`);
8978
+ return {
8979
+ packageDir,
8980
+ resolvedVersion
8981
+ };
8982
+ }
8945
8983
  return withLock(`package:${packageName}:${resolvedVersion}`, async () => {
8946
- if (!existsSync(packageDir)) {
8984
+ if (!isPackageComplete(packageDir)) {
8947
8985
  console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
8948
- await mkdir(packageDir, { recursive: true });
8949
- await pacote.extract(`${packageName}@${resolvedVersion}`, packageDir, { cache: cachePath });
8986
+ const downloadStart = Date.now();
8987
+ const tempDir = join(baseDir, `.tmp-${resolvedVersion}-${Math.random().toString(36).slice(2)}`);
8988
+ await mkdir(tempDir, { recursive: true });
8989
+ try {
8990
+ await pacote.extract(`${packageName}@${resolvedVersion}`, tempDir, { cache: cachePath });
8991
+ await resolveEntryPoint(tempDir, packageName);
8992
+ if (existsSync(packageDir)) await rm(packageDir, {
8993
+ recursive: true,
8994
+ force: true
8995
+ }).catch(() => {});
8996
+ try {
8997
+ await rename(tempDir, packageDir);
8998
+ } catch (err) {
8999
+ if (isPackageComplete(packageDir)) console.log(`[Fetcher] Destination ${packageDir} already exists and is valid.`);
9000
+ else throw err;
9001
+ }
9002
+ console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`);
9003
+ } catch (err) {
9004
+ await rm(tempDir, {
9005
+ recursive: true,
9006
+ force: true
9007
+ }).catch(() => {});
9008
+ throw err;
9009
+ }
8950
9010
  }
9011
+ const entryStart = Date.now();
8951
9012
  const entryPoint = await resolveEntryPoint(packageDir, packageName);
8952
- if (options?.installDeps) await installDependencies(packageDir, packageName, options);
9013
+ console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`);
9014
+ if (options?.installDeps) {
9015
+ const depsStart = Date.now();
9016
+ await installDependencies(packageDir, packageName, options);
9017
+ console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`);
9018
+ }
9019
+ console.log(`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`);
8953
9020
  return {
8954
9021
  packageDir,
8955
9022
  resolvedVersion,
@@ -8988,12 +9055,16 @@ async function runPnpm(cwd, options) {
8988
9055
  * Installs a specific version of Node.js if not already present.
8989
9056
  */
8990
9057
  async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
9058
+ const checkStart = Date.now();
8991
9059
  const isWindows = process.platform === "win32";
8992
9060
  const nodeDir = context.getThirdPartyPath("node", version);
8993
9061
  const finalNodePath = join(nodeDir, isWindows ? "node.exe" : "bin/node");
8994
- if (existsSync(finalNodePath)) return finalNodePath;
9062
+ if (isNodeJSComplete(finalNodePath)) {
9063
+ console.log(`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`);
9064
+ return finalNodePath;
9065
+ }
8995
9066
  return withLock(`node:${version}`, async () => {
8996
- if (existsSync(finalNodePath)) return finalNodePath;
9067
+ if (isNodeJSComplete(finalNodePath)) return finalNodePath;
8997
9068
  const arch = process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : "x86";
8998
9069
  const platform = isWindows ? "win" : process.platform === "darwin" ? "osx" : "linux";
8999
9070
  const extension = isWindows ? "zip" : "tar.gz";
@@ -9003,9 +9074,12 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
9003
9074
  const archivePath = join(tempDir, fileName);
9004
9075
  sendStartupProgress(`Downloading Node.js v${version}...`);
9005
9076
  console.log(`Downloading Node.js from ${downloadUrl}...`);
9077
+ const dlStart = Date.now();
9006
9078
  await downloadFile(downloadUrl, archivePath);
9079
+ console.log(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
9007
9080
  sendStartupProgress(`Extracting Node.js v${version}...`);
9008
9081
  console.log(`Extracting Node.js to ${tempDir}...`);
9082
+ const extStart = Date.now();
9009
9083
  const extractTempDir = join(tempDir, "extracted");
9010
9084
  await mkdir(extractTempDir, { recursive: true });
9011
9085
  if (extension === "zip") await extractZip(archivePath, extractTempDir);
@@ -9013,17 +9087,35 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
9013
9087
  const nodeSubDir = (await readdir(extractTempDir)).find((entry) => entry.startsWith(`node-v${version}`));
9014
9088
  if (!nodeSubDir) throw new Error(`Could not find extracted Node.js directory`);
9015
9089
  const sourceDir = join(extractTempDir, nodeSubDir);
9016
- await mkdir(dirname(nodeDir), { recursive: true });
9017
- await rm(nodeDir, {
9018
- recursive: true,
9019
- force: true
9020
- });
9021
- await cp(sourceDir, nodeDir, { recursive: true });
9022
- await rm(tempDir, {
9023
- recursive: true,
9024
- force: true
9025
- });
9026
- if (!isWindows) await chmod(finalNodePath, 493).catch(() => {});
9090
+ const parentDir = dirname(nodeDir);
9091
+ await mkdir(parentDir, { recursive: true });
9092
+ const tempNodeDir = join(parentDir, `.tmp-node-${version}-${Math.random().toString(36).slice(2)}`);
9093
+ await mkdir(tempNodeDir, { recursive: true });
9094
+ try {
9095
+ await cp(sourceDir, tempNodeDir, { recursive: true });
9096
+ if (!isWindows) await chmod(join(tempNodeDir, "bin/node"), 493).catch(() => {});
9097
+ if (existsSync(nodeDir)) await rm(nodeDir, {
9098
+ recursive: true,
9099
+ force: true
9100
+ }).catch(() => {});
9101
+ try {
9102
+ await rename(tempNodeDir, nodeDir);
9103
+ } catch (err) {
9104
+ if (isNodeJSComplete(finalNodePath)) console.log(`[Fetcher] Node.js directory already exists and is valid.`);
9105
+ else throw err;
9106
+ }
9107
+ console.log(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
9108
+ } finally {
9109
+ await rm(tempNodeDir, {
9110
+ recursive: true,
9111
+ force: true
9112
+ }).catch(() => {});
9113
+ await rm(tempDir, {
9114
+ recursive: true,
9115
+ force: true
9116
+ }).catch(() => {});
9117
+ }
9118
+ console.log(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
9027
9119
  return finalNodePath;
9028
9120
  });
9029
9121
  }
@@ -9031,32 +9123,56 @@ async function ensureNodeJS(context, version = DEFAULT_NODE_VERSION) {
9031
9123
  * Installs the PNPM package from npm if not already present.
9032
9124
  */
9033
9125
  async function ensurePNPM(context, version = DEFAULT_PNPM_VERSION) {
9126
+ const checkStart = Date.now();
9034
9127
  const pnpmPath = join(context.getPackagesPath("pnpm", version), "bin", "pnpm.cjs");
9035
- if (existsSync(pnpmPath)) return pnpmPath;
9128
+ if (existsSync(pnpmPath)) {
9129
+ console.log(`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`);
9130
+ return pnpmPath;
9131
+ }
9036
9132
  return withLock(`pnpm:${version}`, async () => {
9037
9133
  if (existsSync(pnpmPath)) return pnpmPath;
9038
9134
  sendStartupProgress(`Checking PNPM v${version}...`);
9039
9135
  const { packageDir } = await fetchPackage("pnpm", version, { context });
9136
+ console.log(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
9040
9137
  return join(packageDir, "bin", "pnpm.cjs");
9041
9138
  });
9042
9139
  }
9043
9140
  async function installDependencies(packageDir, packageName, options) {
9141
+ const start = Date.now();
9044
9142
  const nodeModulesPath = join(packageDir, "node_modules");
9045
- if (existsSync(nodeModulesPath)) try {
9046
- if ((await readdir(nodeModulesPath)).length === 0) console.warn(`[Fetcher] ${packageName}: node_modules exists but is empty. Re-installing...`);
9047
- } catch (e) {}
9048
- console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
9143
+ if (isDependenciesInstalledSync(packageDir)) {
9144
+ console.log(`[Fetcher] ${packageName}: Dependencies already installed, skipping.`);
9145
+ return;
9146
+ }
9147
+ const tempDir = `${packageDir}.tmp-deps-${Math.random().toString(36).slice(2)}`;
9148
+ await mkdir(tempDir, { recursive: true });
9049
9149
  try {
9050
- const { all } = await runPnpm(packageDir, {
9150
+ await cp(join(packageDir, "package.json"), join(tempDir, "package.json"));
9151
+ console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
9152
+ const pnpmStart = Date.now();
9153
+ const { all } = await runPnpm(tempDir, {
9051
9154
  signal: options.signal,
9052
9155
  context: options.context
9053
9156
  });
9157
+ console.log(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
9054
9158
  if (all) console.log(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
9055
- console.log(`[Fetcher] ${packageName}: Dependencies installed successfully.`);
9159
+ const tempNodeModules = join(tempDir, "node_modules");
9160
+ if (existsSync(nodeModulesPath)) await rm(nodeModulesPath, {
9161
+ recursive: true,
9162
+ force: true
9163
+ }).catch(() => {});
9164
+ const renameStart = Date.now();
9165
+ await rename(tempNodeModules, nodeModulesPath);
9166
+ console.log(`[Fetcher] ${packageName}: Dependencies installed successfully (rename took ${Date.now() - renameStart}ms, total installDependencies took ${Date.now() - start}ms).`);
9056
9167
  } catch (err) {
9057
9168
  console.error(`[Fetcher] ${packageName}: CRITICAL ERROR during dependency installation: ${err.message}`);
9058
9169
  if (err.all) console.error(`[Fetcher] ${packageName}: Error details:\n${err.all}`);
9059
9170
  throw new Error(`Failed to install dependencies for ${packageName}. See logs for details.`);
9171
+ } finally {
9172
+ await rm(tempDir, {
9173
+ recursive: true,
9174
+ force: true
9175
+ }).catch(() => {});
9060
9176
  }
9061
9177
  }
9062
9178
  async function fetchPipelabAsset(packageName, versionOrRange, options) {
@@ -9069,7 +9185,7 @@ async function fetchPipelabAsset(packageName, versionOrRange, options) {
9069
9185
  }
9070
9186
  async function fetchPipelabPlugin(pluginName, versionOrRange, options) {
9071
9187
  const { packageDir, isLocal, entryPoint } = await fetchPackage(pluginName, versionOrRange, {
9072
- installDeps: true,
9188
+ installDeps: false,
9073
9189
  ...options
9074
9190
  });
9075
9191
  let finalEntryPoint = entryPoint;
@@ -9342,11 +9458,15 @@ const DEFAULT_PLUGIN_IDS = [
9342
9458
  "netlify"
9343
9459
  ];
9344
9460
  const loadPipelabPlugin = async (id, options) => {
9461
+ const start = Date.now();
9345
9462
  try {
9346
- const { packageDir, entryPoint } = await fetchPipelabPlugin(`@pipelab/plugin-${id}`, "latest", {
9463
+ const packageName = `@pipelab/plugin-${id}`;
9464
+ const fetchStart = Date.now();
9465
+ const { packageDir, entryPoint } = await fetchPipelabPlugin(packageName, options.context.releaseTag, {
9347
9466
  context: options.context,
9348
- installDeps: true
9467
+ installDeps: false
9349
9468
  });
9469
+ const fetchDuration = Date.now() - fetchStart;
9350
9470
  console.log(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
9351
9471
  if (!existsSync(entryPoint)) {
9352
9472
  console.error(`[Plugins] [${id}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
@@ -9355,25 +9475,46 @@ const loadPipelabPlugin = async (id, options) => {
9355
9475
  console.log(`[Plugins] [${id}] Directory contents:`, files);
9356
9476
  } catch (e) {}
9357
9477
  }
9478
+ const importStart = Date.now();
9358
9479
  const pluginModule = await import(pathToFileURL(entryPoint).href);
9359
- console.log(`[Plugins] [${id}] Successfully loaded from: ${packageDir}`);
9480
+ const importDuration = Date.now() - importStart;
9481
+ const totalDuration = Date.now() - start;
9482
+ console.log(`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`);
9360
9483
  return pluginModule.default;
9361
9484
  } catch (e) {
9362
- console.error(`[Plugins] [${id}] CRITICAL: Failed to load:`, e);
9485
+ console.error(`[Plugins] [${id}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
9363
9486
  if (e.code === "ERR_MODULE_NOT_FOUND") console.error(`[Plugins] [${id}] This usually means a dependency is missing in the plugin's node_modules.`);
9364
9487
  return null;
9365
9488
  }
9366
9489
  };
9367
9490
  const builtInPlugins = async (options) => {
9368
- console.log("[Plugins] Finalizing default plugins list...");
9491
+ console.log("[Plugins] Starting background plugin loading...");
9369
9492
  sendStartupProgress("Preparing environment...");
9493
+ const envStart = Date.now();
9370
9494
  await Promise.all([ensureNodeJS(options.context), ensurePNPM(options.context)]);
9371
- const filtered = (await Promise.allSettled(DEFAULT_PLUGIN_IDS.map(async (id) => {
9372
- sendStartupProgress(`Loading plugin: ${id}`);
9373
- return loadPipelabPlugin(id, options);
9374
- }))).filter((r) => r.status === "fulfilled").map((r) => r.value).filter(Boolean).flat();
9375
- console.log(`[Plugins] Successfully loaded ${filtered.length} default plugins`);
9376
- return filtered;
9495
+ console.log(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
9496
+ const { usePlugins } = await import("@pipelab/shared");
9497
+ const { registerPlugins } = usePlugins();
9498
+ const { webSocketServer } = await import("./index.mjs");
9499
+ (async () => {
9500
+ const totalStart = Date.now();
9501
+ for (const id of DEFAULT_PLUGIN_IDS) {
9502
+ sendStartupProgress(`Loading plugin: ${id}`);
9503
+ const pluginStart = Date.now();
9504
+ const plugin = await loadPipelabPlugin(id, options);
9505
+ console.log(`[Plugins] [${id}] loadPipelabPlugin loop step took ${Date.now() - pluginStart}ms`);
9506
+ if (plugin) {
9507
+ registerPlugins([plugin]);
9508
+ webSocketServer.broadcast("plugin:loaded", { plugin });
9509
+ }
9510
+ }
9511
+ console.log(`[Plugins] All default plugins loaded in ${Date.now() - totalStart}ms.`);
9512
+ sendStartupProgress("All plugins loaded.");
9513
+ setTimeout(() => {
9514
+ webSocketServer.broadcast("startup:progress", { type: "ready" });
9515
+ }, 2e3);
9516
+ })();
9517
+ return [];
9377
9518
  };
9378
9519
  //#endregion
9379
9520
  //#region src/utils.ts
@@ -10268,7 +10409,7 @@ const registerAllHandlers = async (options) => {
10268
10409
  registerAuthHandlers(context);
10269
10410
  registerSystemHandlers(options);
10270
10411
  const { registerPlugins } = usePlugins();
10271
- registerPlugins(await builtInPlugins({ context }));
10412
+ builtInPlugins({ context });
10272
10413
  };
10273
10414
  //#endregion
10274
10415
  //#region ../../node_modules/klona/dist/index.mjs