@pipelab/core-node 1.0.0-beta.10 → 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/CHANGELOG.md +9 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +68 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/scratch/simulate-updates.ts +6 -4
- package/src/plugins-registry.ts +26 -7
- package/src/utils/remote.ts +79 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipelab/core-node",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.11",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Pipelab automation engine for Node.js",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"type-fest": "4.39.0",
|
|
41
41
|
"ws": "8.18.3",
|
|
42
42
|
"yauzl": "2.10.0",
|
|
43
|
-
"@pipelab/constants": "1.0.0-beta.
|
|
44
|
-
"@pipelab/shared": "1.0.0-beta.
|
|
43
|
+
"@pipelab/constants": "1.0.0-beta.9",
|
|
44
|
+
"@pipelab/shared": "1.0.0-beta.7"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/adm-zip": "0.5.7",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@types/yauzl": "2.10.3",
|
|
55
55
|
"tsdown": "0.21.2",
|
|
56
56
|
"typescript": "^5.0.0",
|
|
57
|
-
"@pipelab/tsconfig": "1.0.0-beta.
|
|
57
|
+
"@pipelab/tsconfig": "1.0.0-beta.7"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsdown",
|
|
@@ -30,7 +30,7 @@ const PACKAGES = [
|
|
|
30
30
|
"@pipelab/asset-discord",
|
|
31
31
|
"@pipelab/asset-electron",
|
|
32
32
|
"@pipelab/asset-netlify",
|
|
33
|
-
"@pipelab/asset-tauri"
|
|
33
|
+
"@pipelab/asset-tauri",
|
|
34
34
|
];
|
|
35
35
|
|
|
36
36
|
async function simulate() {
|
|
@@ -44,7 +44,9 @@ async function simulate() {
|
|
|
44
44
|
const stableApp = await fetchLatestDesktopRelease({ allowPrerelease: false });
|
|
45
45
|
const betaApp = await fetchLatestDesktopRelease({ allowPrerelease: true });
|
|
46
46
|
|
|
47
|
-
console.log(
|
|
47
|
+
console.log(
|
|
48
|
+
`[STABLE] Resolved version: ${stableApp ? stableApp.tag_name : "None (Stable users protected)"}`,
|
|
49
|
+
);
|
|
48
50
|
console.log(`[BETA] Resolved version: ${betaApp ? betaApp.tag_name : "None"}`);
|
|
49
51
|
} catch (error) {
|
|
50
52
|
console.error("Failed to fetch desktop releases from GitHub:", error);
|
|
@@ -99,14 +101,14 @@ async function simulate() {
|
|
|
99
101
|
package: pkg,
|
|
100
102
|
stable: stableVersion,
|
|
101
103
|
beta: betaVersion,
|
|
102
|
-
notes
|
|
104
|
+
notes,
|
|
103
105
|
});
|
|
104
106
|
} catch (error: any) {
|
|
105
107
|
results.push({
|
|
106
108
|
package: pkg,
|
|
107
109
|
stable: "ERROR",
|
|
108
110
|
beta: "ERROR",
|
|
109
|
-
notes: `Failed to fetch: ${error.message}
|
|
111
|
+
notes: `Failed to fetch: ${error.message}`,
|
|
110
112
|
});
|
|
111
113
|
}
|
|
112
114
|
}
|
package/src/plugins-registry.ts
CHANGED
|
@@ -21,12 +21,19 @@ const DEFAULT_PLUGIN_IDS = [
|
|
|
21
21
|
];
|
|
22
22
|
|
|
23
23
|
export const loadPipelabPlugin = async (id: string, options: { context: PipelabContext }) => {
|
|
24
|
+
const start = Date.now();
|
|
24
25
|
try {
|
|
25
26
|
const packageName = `@pipelab/plugin-${id}`;
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const fetchStart = Date.now();
|
|
28
|
+
const { packageDir, entryPoint } = await fetchPipelabPlugin(
|
|
29
|
+
packageName,
|
|
30
|
+
options.context.releaseTag,
|
|
31
|
+
{
|
|
32
|
+
context: options.context,
|
|
33
|
+
installDeps: false,
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
const fetchDuration = Date.now() - fetchStart;
|
|
30
37
|
|
|
31
38
|
console.log(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
|
|
32
39
|
if (!existsSync(entryPoint)) {
|
|
@@ -37,11 +44,16 @@ export const loadPipelabPlugin = async (id: string, options: { context: PipelabC
|
|
|
37
44
|
} catch (e) {}
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
const importStart = Date.now();
|
|
40
48
|
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
41
|
-
|
|
49
|
+
const importDuration = Date.now() - importStart;
|
|
50
|
+
const totalDuration = Date.now() - start;
|
|
51
|
+
console.log(
|
|
52
|
+
`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
|
|
53
|
+
);
|
|
42
54
|
return pluginModule.default;
|
|
43
55
|
} catch (e: any) {
|
|
44
|
-
console.error(`[Plugins] [${id}] CRITICAL: Failed to load:`, e);
|
|
56
|
+
console.error(`[Plugins] [${id}] CRITICAL: Failed to load after ${Date.now() - start}ms:`, e);
|
|
45
57
|
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
46
58
|
console.error(
|
|
47
59
|
`[Plugins] [${id}] This usually means a dependency is missing in the plugin's node_modules.`,
|
|
@@ -56,7 +68,9 @@ export const builtInPlugins = async (options: { context: PipelabContext }) => {
|
|
|
56
68
|
|
|
57
69
|
// Pre-ensure Node.js and PNPM once in parallel so plugins don't have to wait for them
|
|
58
70
|
sendStartupProgress("Preparing environment...");
|
|
71
|
+
const envStart = Date.now();
|
|
59
72
|
await Promise.all([ensureNodeJS(options.context), ensurePNPM(options.context)]);
|
|
73
|
+
console.log(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
|
|
60
74
|
|
|
61
75
|
const { usePlugins } = await import("@pipelab/shared");
|
|
62
76
|
const { registerPlugins } = usePlugins();
|
|
@@ -64,15 +78,20 @@ export const builtInPlugins = async (options: { context: PipelabContext }) => {
|
|
|
64
78
|
|
|
65
79
|
// Load plugins asynchronously in the background
|
|
66
80
|
(async () => {
|
|
81
|
+
const totalStart = Date.now();
|
|
67
82
|
for (const id of DEFAULT_PLUGIN_IDS) {
|
|
68
83
|
sendStartupProgress(`Loading plugin: ${id}`);
|
|
84
|
+
const pluginStart = Date.now();
|
|
69
85
|
const plugin = await loadPipelabPlugin(id, options);
|
|
86
|
+
console.log(
|
|
87
|
+
`[Plugins] [${id}] loadPipelabPlugin loop step took ${Date.now() - pluginStart}ms`,
|
|
88
|
+
);
|
|
70
89
|
if (plugin) {
|
|
71
90
|
registerPlugins([plugin]);
|
|
72
91
|
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
73
92
|
}
|
|
74
93
|
}
|
|
75
|
-
console.log(
|
|
94
|
+
console.log(`[Plugins] All default plugins loaded in ${Date.now() - totalStart}ms.`);
|
|
76
95
|
sendStartupProgress("All plugins loaded.");
|
|
77
96
|
setTimeout(() => {
|
|
78
97
|
webSocketServer.broadcast("startup:progress", { type: "ready" });
|
package/src/utils/remote.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { dirname, delimiter, join } from "node:path";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
mkdir,
|
|
5
|
+
readdir,
|
|
6
|
+
readFile,
|
|
7
|
+
writeFile,
|
|
8
|
+
access,
|
|
9
|
+
chmod,
|
|
10
|
+
rm,
|
|
11
|
+
cp,
|
|
12
|
+
rename,
|
|
13
|
+
} from "node:fs/promises";
|
|
4
14
|
import { existsSync, constants, statSync, readdirSync } from "node:fs";
|
|
5
15
|
import pacote from "pacote";
|
|
6
16
|
import semver from "semver";
|
|
@@ -80,12 +90,16 @@ export async function fetchPackage(
|
|
|
80
90
|
isLocal?: boolean;
|
|
81
91
|
entryPoint?: string;
|
|
82
92
|
}> {
|
|
93
|
+
const start = Date.now();
|
|
83
94
|
// 0. Check for local monorepo package in development
|
|
84
95
|
if (isDev && projectRoot && process.env.PIPELAB_FORCE_NPM !== "true") {
|
|
85
96
|
if (packageName.startsWith("@pipelab/")) {
|
|
97
|
+
const localStart = Date.now();
|
|
86
98
|
const local = await tryResolveMonorepoPackage(packageName);
|
|
87
99
|
if (local) {
|
|
88
|
-
console.log(
|
|
100
|
+
console.log(
|
|
101
|
+
`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`,
|
|
102
|
+
);
|
|
89
103
|
return {
|
|
90
104
|
...local,
|
|
91
105
|
resolvedVersion: "workspace",
|
|
@@ -99,6 +113,7 @@ export async function fetchPackage(
|
|
|
99
113
|
let resolvedVersion: string;
|
|
100
114
|
|
|
101
115
|
console.log(`[Fetcher] Resolving ${packageName}@${versionOrRange || "latest"}...`);
|
|
116
|
+
const resolveStart = Date.now();
|
|
102
117
|
|
|
103
118
|
try {
|
|
104
119
|
// 1. Resolve version/range using npm with session-wide memoization and disk cache
|
|
@@ -122,12 +137,20 @@ export async function fetchPackage(
|
|
|
122
137
|
);
|
|
123
138
|
}
|
|
124
139
|
resolvedVersion = foundVersion;
|
|
125
|
-
console.log(
|
|
140
|
+
console.log(
|
|
141
|
+
`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`,
|
|
142
|
+
);
|
|
126
143
|
} catch (error) {
|
|
127
|
-
console.warn(
|
|
144
|
+
console.warn(
|
|
145
|
+
`[Fetcher] ${packageName}: remote resolution failed (${Date.now() - resolveStart}ms), trying local fallback...`,
|
|
146
|
+
);
|
|
147
|
+
const fallbackStart = Date.now();
|
|
128
148
|
const fallbackVersion = await tryLocalFallback(versionOrRange, error, baseDir, packageName);
|
|
129
149
|
if (fallbackVersion) {
|
|
130
150
|
resolvedVersion = fallbackVersion;
|
|
151
|
+
console.log(
|
|
152
|
+
`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
|
|
153
|
+
);
|
|
131
154
|
} else {
|
|
132
155
|
throw error;
|
|
133
156
|
}
|
|
@@ -136,12 +159,17 @@ export async function fetchPackage(
|
|
|
136
159
|
const cachePath = join(ctx.userDataPath, "cache", "pacote");
|
|
137
160
|
const packageDir = join(baseDir, resolvedVersion);
|
|
138
161
|
|
|
162
|
+
const checkStart = Date.now();
|
|
139
163
|
// If the package already exists and we don't need to install dependencies (or they are already installed), return immediately
|
|
140
164
|
const isInstalled = options?.installDeps
|
|
141
|
-
?
|
|
165
|
+
? isPackageComplete(packageDir) && isDependenciesInstalledSync(packageDir)
|
|
142
166
|
: isPackageComplete(packageDir);
|
|
167
|
+
const checkDuration = Date.now() - checkStart;
|
|
143
168
|
|
|
144
169
|
if (isInstalled) {
|
|
170
|
+
console.log(
|
|
171
|
+
`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`,
|
|
172
|
+
);
|
|
145
173
|
return { packageDir, resolvedVersion };
|
|
146
174
|
}
|
|
147
175
|
|
|
@@ -149,8 +177,12 @@ export async function fetchPackage(
|
|
|
149
177
|
return withLock(lockKey, async () => {
|
|
150
178
|
if (!isPackageComplete(packageDir)) {
|
|
151
179
|
console.log(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
|
|
152
|
-
|
|
153
|
-
|
|
180
|
+
const downloadStart = Date.now();
|
|
181
|
+
|
|
182
|
+
const tempDir = join(
|
|
183
|
+
baseDir,
|
|
184
|
+
`.tmp-${resolvedVersion}-${Math.random().toString(36).slice(2)}`,
|
|
185
|
+
);
|
|
154
186
|
await mkdir(tempDir, { recursive: true });
|
|
155
187
|
try {
|
|
156
188
|
await pacote.extract(`${packageName}@${resolvedVersion}`, tempDir, {
|
|
@@ -175,6 +207,9 @@ export async function fetchPackage(
|
|
|
175
207
|
throw err;
|
|
176
208
|
}
|
|
177
209
|
}
|
|
210
|
+
console.log(
|
|
211
|
+
`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`,
|
|
212
|
+
);
|
|
178
213
|
} catch (err) {
|
|
179
214
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
180
215
|
throw err;
|
|
@@ -182,12 +217,23 @@ export async function fetchPackage(
|
|
|
182
217
|
}
|
|
183
218
|
|
|
184
219
|
// 2. Resolve entry point from package.json for downloaded package
|
|
220
|
+
const entryStart = Date.now();
|
|
185
221
|
const entryPoint = await resolveEntryPoint(packageDir, packageName);
|
|
222
|
+
console.log(
|
|
223
|
+
`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`,
|
|
224
|
+
);
|
|
186
225
|
|
|
187
226
|
if (options?.installDeps) {
|
|
227
|
+
const depsStart = Date.now();
|
|
188
228
|
await installDependencies(packageDir, packageName, options);
|
|
229
|
+
console.log(
|
|
230
|
+
`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`,
|
|
231
|
+
);
|
|
189
232
|
}
|
|
190
233
|
|
|
234
|
+
console.log(
|
|
235
|
+
`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`,
|
|
236
|
+
);
|
|
191
237
|
return { packageDir, resolvedVersion, entryPoint };
|
|
192
238
|
});
|
|
193
239
|
}
|
|
@@ -245,11 +291,15 @@ export async function runPnpm(
|
|
|
245
291
|
* Installs a specific version of Node.js if not already present.
|
|
246
292
|
*/
|
|
247
293
|
export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NODE_VERSION) {
|
|
294
|
+
const checkStart = Date.now();
|
|
248
295
|
const isWindows = process.platform === "win32";
|
|
249
296
|
const nodeDir = context.getThirdPartyPath("node", version);
|
|
250
297
|
const finalNodePath = join(nodeDir, isWindows ? "node.exe" : "bin/node");
|
|
251
298
|
|
|
252
299
|
if (isNodeJSComplete(finalNodePath)) {
|
|
300
|
+
console.log(
|
|
301
|
+
`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`,
|
|
302
|
+
);
|
|
253
303
|
return finalNodePath;
|
|
254
304
|
}
|
|
255
305
|
|
|
@@ -269,10 +319,13 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
269
319
|
|
|
270
320
|
sendStartupProgress(`Downloading Node.js v${version}...`);
|
|
271
321
|
console.log(`Downloading Node.js from ${downloadUrl}...`);
|
|
322
|
+
const dlStart = Date.now();
|
|
272
323
|
await downloadFile(downloadUrl, archivePath);
|
|
324
|
+
console.log(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
|
|
273
325
|
|
|
274
326
|
sendStartupProgress(`Extracting Node.js v${version}...`);
|
|
275
327
|
console.log(`Extracting Node.js to ${tempDir}...`);
|
|
328
|
+
const extStart = Date.now();
|
|
276
329
|
const extractTempDir = join(tempDir, "extracted");
|
|
277
330
|
await mkdir(extractTempDir, { recursive: true });
|
|
278
331
|
|
|
@@ -290,7 +343,10 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
290
343
|
const parentDir = dirname(nodeDir);
|
|
291
344
|
await mkdir(parentDir, { recursive: true });
|
|
292
345
|
|
|
293
|
-
const tempNodeDir = join(
|
|
346
|
+
const tempNodeDir = join(
|
|
347
|
+
parentDir,
|
|
348
|
+
`.tmp-node-${version}-${Math.random().toString(36).slice(2)}`,
|
|
349
|
+
);
|
|
294
350
|
await mkdir(tempNodeDir, { recursive: true });
|
|
295
351
|
|
|
296
352
|
try {
|
|
@@ -313,11 +369,13 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
313
369
|
throw err;
|
|
314
370
|
}
|
|
315
371
|
}
|
|
372
|
+
console.log(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
|
|
316
373
|
} finally {
|
|
317
374
|
await rm(tempNodeDir, { recursive: true, force: true }).catch(() => {});
|
|
318
375
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
319
376
|
}
|
|
320
377
|
|
|
378
|
+
console.log(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
|
|
321
379
|
return finalNodePath;
|
|
322
380
|
});
|
|
323
381
|
}
|
|
@@ -326,10 +384,14 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
326
384
|
* Installs the PNPM package from npm if not already present.
|
|
327
385
|
*/
|
|
328
386
|
export async function ensurePNPM(context: PipelabContext, version = DEFAULT_PNPM_VERSION) {
|
|
387
|
+
const checkStart = Date.now();
|
|
329
388
|
const pnpmDir = context.getPackagesPath("pnpm", version);
|
|
330
389
|
const pnpmPath = join(pnpmDir, "bin", "pnpm.cjs");
|
|
331
390
|
|
|
332
391
|
if (existsSync(pnpmPath)) {
|
|
392
|
+
console.log(
|
|
393
|
+
`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`,
|
|
394
|
+
);
|
|
333
395
|
return pnpmPath;
|
|
334
396
|
}
|
|
335
397
|
|
|
@@ -340,11 +402,13 @@ export async function ensurePNPM(context: PipelabContext, version = DEFAULT_PNPM
|
|
|
340
402
|
const { packageDir } = await fetchPackage("pnpm", version, {
|
|
341
403
|
context,
|
|
342
404
|
});
|
|
405
|
+
console.log(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
|
|
343
406
|
return join(packageDir, "bin", "pnpm.cjs");
|
|
344
407
|
});
|
|
345
408
|
}
|
|
346
409
|
|
|
347
410
|
async function installDependencies(packageDir: string, packageName: string, options: FetchOptions) {
|
|
411
|
+
const start = Date.now();
|
|
348
412
|
const nodeModulesPath = join(packageDir, "node_modules");
|
|
349
413
|
|
|
350
414
|
if (isDependenciesInstalledSync(packageDir)) {
|
|
@@ -359,12 +423,14 @@ async function installDependencies(packageDir: string, packageName: string, opti
|
|
|
359
423
|
try {
|
|
360
424
|
// Copy package.json to tempDir so pnpm can install dependencies
|
|
361
425
|
await cp(join(packageDir, "package.json"), join(tempDir, "package.json"));
|
|
362
|
-
|
|
426
|
+
|
|
363
427
|
console.log(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
|
|
428
|
+
const pnpmStart = Date.now();
|
|
364
429
|
const { all } = await runPnpm(tempDir, {
|
|
365
430
|
signal: options.signal,
|
|
366
431
|
context: options.context,
|
|
367
432
|
});
|
|
433
|
+
console.log(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
|
|
368
434
|
|
|
369
435
|
if (all) console.log(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
|
|
370
436
|
|
|
@@ -373,8 +439,11 @@ async function installDependencies(packageDir: string, packageName: string, opti
|
|
|
373
439
|
if (existsSync(nodeModulesPath)) {
|
|
374
440
|
await rm(nodeModulesPath, { recursive: true, force: true }).catch(() => {});
|
|
375
441
|
}
|
|
442
|
+
const renameStart = Date.now();
|
|
376
443
|
await rename(tempNodeModules, nodeModulesPath);
|
|
377
|
-
console.log(
|
|
444
|
+
console.log(
|
|
445
|
+
`[Fetcher] ${packageName}: Dependencies installed successfully (rename took ${Date.now() - renameStart}ms, total installDependencies took ${Date.now() - start}ms).`,
|
|
446
|
+
);
|
|
378
447
|
} catch (err: any) {
|
|
379
448
|
console.error(
|
|
380
449
|
`[Fetcher] ${packageName}: CRITICAL ERROR during dependency installation: ${err.message}`,
|