@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/.oxfmtrc.json +3 -0
- package/CHANGELOG.md +83 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +209 -68
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/scratch/simulate-updates.ts +120 -0
- package/src/config.ts +10 -15
- package/src/context.ts +8 -2
- package/src/handler-func.ts +6 -6
- package/src/handlers/build-history.ts +6 -8
- package/src/handlers/history.ts +6 -8
- package/src/handlers/index.ts +2 -2
- package/src/plugins-registry.ts +48 -24
- package/src/runner.ts +28 -22
- package/src/server.ts +4 -2
- package/src/utils/fs-extras.ts +10 -1
- package/src/utils/github.ts +10 -6
- package/src/utils/remote.ts +206 -53
- package/src/utils.ts +1 -1
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
|
-
|
|
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
|
-
})()
|
|
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, {
|
|
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
|
-
|
|
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
|
|
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",
|
|
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)
|
|
8937
|
-
|
|
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
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
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 (!
|
|
8984
|
+
if (!isPackageComplete(packageDir)) {
|
|
8947
8985
|
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
|
|
8948
|
-
|
|
8949
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
9017
|
-
await
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
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))
|
|
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 (
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
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]
|
|
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
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
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
|
-
|
|
10412
|
+
builtInPlugins({ context });
|
|
10272
10413
|
};
|
|
10273
10414
|
//#endregion
|
|
10274
10415
|
//#region ../../node_modules/klona/dist/index.mjs
|