@pipelab/core-node 1.0.1-latest.34 → 1.0.1-latest.36
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/CHANGELOG.md +18 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +95 -66
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/plugins-registry.ts +7 -4
- package/src/server.ts +5 -0
- package/src/utils/remote.ts +99 -66
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipelab/core-node",
|
|
3
|
-
"version": "1.0.1-latest.
|
|
3
|
+
"version": "1.0.1-latest.36",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Pipelab automation engine for Node.js",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"type-fest": "4.39.0",
|
|
40
40
|
"ws": "8.18.3",
|
|
41
41
|
"yauzl": "2.10.0",
|
|
42
|
-
"@pipelab/constants": "1.0.1-latest.
|
|
43
|
-
"@pipelab/shared": "2.0.1-latest.
|
|
42
|
+
"@pipelab/constants": "1.0.1-latest.32",
|
|
43
|
+
"@pipelab/shared": "2.0.1-latest.33"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/adm-zip": "0.5.7",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@types/yauzl": "2.10.3",
|
|
54
54
|
"tsdown": "0.21.2",
|
|
55
55
|
"typescript": "^5.0.0",
|
|
56
|
-
"@pipelab/tsconfig": "1.0.1-latest.
|
|
56
|
+
"@pipelab/tsconfig": "1.0.1-latest.32"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsdown",
|
package/src/plugins-registry.ts
CHANGED
|
@@ -53,11 +53,14 @@ export const loadPipelabPlugin = async (id: string, options: { context: PipelabC
|
|
|
53
53
|
|
|
54
54
|
export const builtInPlugins = async (options: { context: PipelabContext }) => {
|
|
55
55
|
console.log("[Plugins] Finalizing default plugins list...");
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
const plugins = [];
|
|
57
|
+
for (const id of DEFAULT_PLUGIN_IDS) {
|
|
58
|
+
const plugin = await loadPipelabPlugin(id, options);
|
|
59
|
+
if (plugin) {
|
|
60
|
+
plugins.push(plugin);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
59
63
|
|
|
60
|
-
const plugins = await Promise.all(promises);
|
|
61
64
|
const filtered = plugins.filter(Boolean).flat();
|
|
62
65
|
console.log(`[Plugins] Successfully loaded ${filtered.length} default plugins`);
|
|
63
66
|
return filtered;
|
package/src/server.ts
CHANGED
|
@@ -76,5 +76,10 @@ export async function serveCommand(options: ServeOptions, version: string, _dirn
|
|
|
76
76
|
registerMigrationHandlers(context);
|
|
77
77
|
|
|
78
78
|
await webSocketServer.start(Number(options.port), server);
|
|
79
|
+
|
|
80
|
+
if (process.send) {
|
|
81
|
+
process.send({ type: "ready" });
|
|
82
|
+
}
|
|
83
|
+
|
|
79
84
|
return server;
|
|
80
85
|
}
|
package/src/utils/remote.ts
CHANGED
|
@@ -8,6 +8,30 @@ import { isDev, projectRoot, PipelabContext } from "../context";
|
|
|
8
8
|
import { execa } from "execa";
|
|
9
9
|
import { downloadFile, extractZip, extractTarGz, generateTempFolder } from "./fs-extras";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* In-memory lock to prevent concurrent operations on the same resource (e.g., downloading Node.js).
|
|
13
|
+
*/
|
|
14
|
+
const activeOperations = new Map<string, Promise<any>>();
|
|
15
|
+
|
|
16
|
+
async function withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {
|
|
17
|
+
const existing = activeOperations.get(key);
|
|
18
|
+
if (existing) {
|
|
19
|
+
console.log(`[Lock] Waiting for concurrent operation on: ${key}`);
|
|
20
|
+
return existing;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const promise = (async () => {
|
|
24
|
+
try {
|
|
25
|
+
return await fn();
|
|
26
|
+
} finally {
|
|
27
|
+
activeOperations.delete(key);
|
|
28
|
+
}
|
|
29
|
+
})();
|
|
30
|
+
|
|
31
|
+
activeOperations.set(key, promise);
|
|
32
|
+
return promise;
|
|
33
|
+
}
|
|
34
|
+
|
|
11
35
|
export type FetchOptions = {
|
|
12
36
|
installDeps?: boolean;
|
|
13
37
|
signal?: AbortSignal;
|
|
@@ -72,36 +96,39 @@ export async function fetchPackage(
|
|
|
72
96
|
}
|
|
73
97
|
}
|
|
74
98
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
const lockKey = `package:${packageName}:${resolvedVersion}`;
|
|
100
|
+
return withLock(lockKey, async () => {
|
|
101
|
+
const packageDir = join(baseDir, resolvedVersion);
|
|
102
|
+
if (!existsSync(packageDir)) {
|
|
103
|
+
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
|
|
104
|
+
await mkdir(packageDir, { recursive: true });
|
|
105
|
+
await pacote.extract(`${packageName}@${resolvedVersion}`, packageDir);
|
|
106
|
+
}
|
|
81
107
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
// 2. Resolve entry point from package.json for downloaded package
|
|
109
|
+
let entryPoint: string | undefined;
|
|
110
|
+
try {
|
|
111
|
+
const pkgPath = join(packageDir, "package.json");
|
|
112
|
+
if (existsSync(pkgPath)) {
|
|
113
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
114
|
+
const main = pkg.module || pkg.main || pkg.publishConfig?.module || pkg.publishConfig?.main;
|
|
115
|
+
if (main) {
|
|
116
|
+
entryPoint = join(packageDir, main);
|
|
117
|
+
} else if (pkg.bin) {
|
|
118
|
+
const binFile = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin)[0];
|
|
119
|
+
if (binFile) entryPoint = join(packageDir, binFile as string);
|
|
120
|
+
}
|
|
94
121
|
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.warn(`[Fetcher] ${packageName}: Failed to parse package.json for entry point resolution:`, e);
|
|
95
124
|
}
|
|
96
|
-
} catch (e) {
|
|
97
|
-
console.warn(`[Fetcher] ${packageName}: Failed to parse package.json for entry point resolution:`, e);
|
|
98
|
-
}
|
|
99
125
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
126
|
+
if (options?.installDeps) {
|
|
127
|
+
await installDependencies(packageDir, packageName, options);
|
|
128
|
+
}
|
|
103
129
|
|
|
104
|
-
|
|
130
|
+
return { packageDir, resolvedVersion, entryPoint };
|
|
131
|
+
});
|
|
105
132
|
}
|
|
106
133
|
|
|
107
134
|
/**
|
|
@@ -148,63 +175,69 @@ export async function runPnpm(
|
|
|
148
175
|
* Installs a specific version of Node.js if not already present.
|
|
149
176
|
*/
|
|
150
177
|
export async function ensureNodeJS(version: string, options: { context: PipelabContext }) {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
178
|
+
const lockKey = `node:${version}`;
|
|
179
|
+
return withLock(lockKey, async () => {
|
|
180
|
+
const ctx = options.context;
|
|
181
|
+
const nodeDir = ctx.getThirdPartyPath("node", version);
|
|
182
|
+
const isWindows = process.platform === "win32";
|
|
183
|
+
const executableName = isWindows ? "node.exe" : "bin/node";
|
|
184
|
+
const finalNodePath = join(nodeDir, executableName);
|
|
156
185
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
186
|
+
try {
|
|
187
|
+
await access(finalNodePath, constants.X_OK);
|
|
188
|
+
return finalNodePath;
|
|
189
|
+
} catch (e) {}
|
|
161
190
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
191
|
+
const arch = process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : "x86";
|
|
192
|
+
const platform = isWindows ? "win" : process.platform === "darwin" ? "osx" : "linux";
|
|
193
|
+
const extension = isWindows ? "zip" : "tar.gz";
|
|
194
|
+
const downloadPlatform = platform === "osx" ? "darwin" : platform;
|
|
166
195
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
196
|
+
const fileName = `node-v${version}-${downloadPlatform}-${arch}.${extension}`;
|
|
197
|
+
const downloadUrl = `https://nodejs.org/dist/v${version}/${fileName}`;
|
|
198
|
+
const tempDir = await generateTempFolder(tmpdir());
|
|
199
|
+
const archivePath = join(tempDir, fileName);
|
|
171
200
|
|
|
172
|
-
|
|
173
|
-
|
|
201
|
+
console.log(`Downloading Node.js from ${downloadUrl}...`);
|
|
202
|
+
await downloadFile(downloadUrl, archivePath);
|
|
174
203
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
204
|
+
console.log(`Extracting Node.js to ${tempDir}...`);
|
|
205
|
+
const extractTempDir = join(tempDir, "extracted");
|
|
206
|
+
await mkdir(extractTempDir, { recursive: true });
|
|
178
207
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
208
|
+
if (extension === "zip") {
|
|
209
|
+
await extractZip(archivePath, extractTempDir);
|
|
210
|
+
} else {
|
|
211
|
+
await extractTarGz(archivePath, extractTempDir);
|
|
212
|
+
}
|
|
184
213
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
214
|
+
const extractedEntries = await readdir(extractTempDir);
|
|
215
|
+
const nodeSubDir = extractedEntries.find((entry) => entry.startsWith(`node-v${version}`));
|
|
216
|
+
if (!nodeSubDir) throw new Error(`Could not find extracted Node.js directory`);
|
|
188
217
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
218
|
+
const sourceDir = join(extractTempDir, nodeSubDir);
|
|
219
|
+
await mkdir(dirname(nodeDir), { recursive: true });
|
|
220
|
+
await rm(nodeDir, { recursive: true, force: true });
|
|
221
|
+
await cp(sourceDir, nodeDir, { recursive: true });
|
|
222
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
194
223
|
|
|
195
|
-
|
|
196
|
-
|
|
224
|
+
if (!isWindows) await chmod(finalNodePath, 0o755).catch(() => {});
|
|
225
|
+
return finalNodePath;
|
|
226
|
+
});
|
|
197
227
|
}
|
|
198
228
|
|
|
199
229
|
/**
|
|
200
230
|
* Installs the PNPM package from npm if not already present.
|
|
201
231
|
*/
|
|
202
232
|
export async function ensurePNPM(version = "10.12.0", options: { context: PipelabContext }) {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
233
|
+
const lockKey = `pnpm:${version}`;
|
|
234
|
+
return withLock(lockKey, async () => {
|
|
235
|
+
const ctx = options.context;
|
|
236
|
+
const { packageDir } = await fetchPackage("pnpm", version, {
|
|
237
|
+
context: ctx,
|
|
238
|
+
});
|
|
239
|
+
return join(packageDir, "bin", "pnpm.cjs");
|
|
206
240
|
});
|
|
207
|
-
return join(packageDir, "bin", "pnpm.cjs");
|
|
208
241
|
}
|
|
209
242
|
|
|
210
243
|
async function installDependencies(packageDir: string, packageName: string, options: FetchOptions) {
|