@pipelab/core-node 1.0.0-beta.32 → 1.0.0-beta.34
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.cjs +246 -256
- package/dist/index.d.cts +29 -28
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +29 -28
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +246 -256
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/handlers/index.ts +6 -1
- package/src/handlers/plugins.ts +4 -19
- package/src/plugins-registry.ts +14 -15
- package/src/runner.ts +1 -1
- package/src/server.ts +0 -1
- package/src/utils/remote.ts +27 -42
- package/src/utils.ts +10 -9
- package/scratch/simulate-updates.ts +0 -120
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.34",
|
|
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.30",
|
|
44
|
+
"@pipelab/shared": "1.0.0-beta.29"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/adm-zip": "0.5.7",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"tsdown": "0.21.2",
|
|
57
57
|
"typescript": "^5.0.0",
|
|
58
58
|
"vitest": "3.1.4",
|
|
59
|
-
"@pipelab/tsconfig": "1.0.0-beta.
|
|
59
|
+
"@pipelab/tsconfig": "1.0.0-beta.28"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsdown",
|
package/src/handlers/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { PipelabContext } from "../context";
|
|
|
15
15
|
export const registerAllHandlers = async (options: {
|
|
16
16
|
version: string;
|
|
17
17
|
context: PipelabContext;
|
|
18
|
+
waitForPlugins?: boolean;
|
|
18
19
|
}) => {
|
|
19
20
|
const context = options.context;
|
|
20
21
|
|
|
@@ -31,9 +32,13 @@ export const registerAllHandlers = async (options: {
|
|
|
31
32
|
|
|
32
33
|
const { registerPlugins } = usePlugins();
|
|
33
34
|
// Execute in the background! The plugins will be dynamically registered and broadcasted to the UI.
|
|
34
|
-
builtInPlugins({
|
|
35
|
+
const pluginsPromise = builtInPlugins({
|
|
35
36
|
context,
|
|
36
37
|
});
|
|
38
|
+
|
|
39
|
+
if (options.waitForPlugins) {
|
|
40
|
+
await pluginsPromise;
|
|
41
|
+
}
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
export { registerShellHandlers } from "./shell";
|
package/src/handlers/plugins.ts
CHANGED
|
@@ -271,27 +271,12 @@ export const registerPluginsHandlers = (context: PipelabContext) => {
|
|
|
271
271
|
});
|
|
272
272
|
|
|
273
273
|
if (isRegistered) {
|
|
274
|
-
|
|
274
|
+
loaded.push(packageName);
|
|
275
|
+
continue;
|
|
275
276
|
}
|
|
276
277
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
`[Plugins] JIT loading plugin "${packageName}@${mappedVersion}" for pipeline...`,
|
|
280
|
-
);
|
|
281
|
-
const plugin = await loadCustomPlugin(packageName, mappedVersion, { context });
|
|
282
|
-
if (plugin) {
|
|
283
|
-
registerPlugins([plugin]);
|
|
284
|
-
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
285
|
-
loaded.push(packageName);
|
|
286
|
-
console.log(`[Plugins] JIT loaded "${packageName}" successfully.`);
|
|
287
|
-
} else {
|
|
288
|
-
console.warn(`[Plugins] JIT load for "${packageName}" returned no plugin.`);
|
|
289
|
-
failed.push(packageName);
|
|
290
|
-
}
|
|
291
|
-
} catch (e: any) {
|
|
292
|
-
console.error(`[Plugins] JIT load failed for "${packageName}":`, e);
|
|
293
|
-
failed.push(packageName);
|
|
294
|
-
}
|
|
278
|
+
console.warn(`[Plugins] Plugin "${packageName}" is required but not loaded at startup.`);
|
|
279
|
+
failed.push(packageName);
|
|
295
280
|
}
|
|
296
281
|
|
|
297
282
|
send({
|
package/src/plugins-registry.ts
CHANGED
|
@@ -80,12 +80,12 @@ export const loadPipelabPlugin = async (id: string, options: { context: PipelabC
|
|
|
80
80
|
);
|
|
81
81
|
const fetchDuration = Date.now() - fetchStart;
|
|
82
82
|
|
|
83
|
-
console.
|
|
83
|
+
console.debug(`[Plugins] [${id}] Attempting to import from: ${entryPoint}`);
|
|
84
84
|
if (!existsSync(entryPoint)) {
|
|
85
85
|
console.error(`[Plugins] [${id}] CRITICAL: Plugin entry point not found at ${entryPoint}`);
|
|
86
86
|
try {
|
|
87
87
|
const files = await readdir(packageDir, { recursive: true });
|
|
88
|
-
console.
|
|
88
|
+
console.debug(`[Plugins] [${id}] Directory contents:`, files);
|
|
89
89
|
} catch (e) {}
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -93,8 +93,7 @@ export const loadPipelabPlugin = async (id: string, options: { context: PipelabC
|
|
|
93
93
|
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
94
94
|
const importDuration = Date.now() - importStart;
|
|
95
95
|
const totalDuration = Date.now() - start;
|
|
96
|
-
console.
|
|
97
|
-
`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
|
|
96
|
+
console.debug(`[Plugins] [${id}] Successfully loaded from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
|
|
98
97
|
);
|
|
99
98
|
|
|
100
99
|
const plugin = pluginModule.default;
|
|
@@ -125,8 +124,7 @@ export const loadCustomPlugin = async (
|
|
|
125
124
|
});
|
|
126
125
|
const fetchDuration = Date.now() - fetchStart;
|
|
127
126
|
|
|
128
|
-
console.
|
|
129
|
-
`[Plugins] [${packageName}] Attempting to import custom plugin from: ${entryPoint}`,
|
|
127
|
+
console.debug(`[Plugins] [${packageName}] Attempting to import custom plugin from: ${entryPoint}`,
|
|
130
128
|
);
|
|
131
129
|
if (!existsSync(entryPoint)) {
|
|
132
130
|
console.error(
|
|
@@ -139,8 +137,7 @@ export const loadCustomPlugin = async (
|
|
|
139
137
|
const pluginModule = await import(pathToFileURL(entryPoint).href);
|
|
140
138
|
const importDuration = Date.now() - importStart;
|
|
141
139
|
const totalDuration = Date.now() - start;
|
|
142
|
-
console.
|
|
143
|
-
`[Plugins] [${packageName}] Successfully loaded custom plugin from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
|
|
140
|
+
console.debug(`[Plugins] [${packageName}] Successfully loaded custom plugin from: ${packageDir} (fetch: ${fetchDuration}ms, import: ${importDuration}ms, total: ${totalDuration}ms)`,
|
|
144
141
|
);
|
|
145
142
|
|
|
146
143
|
const plugin = pluginModule.default;
|
|
@@ -204,13 +201,13 @@ export async function findInstalledPlugins(
|
|
|
204
201
|
}
|
|
205
202
|
|
|
206
203
|
export const builtInPlugins = async (options: { context: PipelabContext }): Promise<void> => {
|
|
207
|
-
console.
|
|
204
|
+
console.debug("[Plugins] Starting background plugin loading...");
|
|
208
205
|
|
|
209
206
|
// Pre-ensure Node.js and PNPM once in parallel so plugins don't have to wait for them
|
|
210
207
|
sendStartupProgress("Preparing environment...");
|
|
211
208
|
const envStart = Date.now();
|
|
212
209
|
await Promise.all([ensureNodeJS(options.context), ensurePNPM(options.context)]);
|
|
213
|
-
console.
|
|
210
|
+
console.debug(`[Plugins] Environment preparation took ${Date.now() - envStart}ms`);
|
|
214
211
|
|
|
215
212
|
const { usePlugins } = await import("@pipelab/shared");
|
|
216
213
|
const { registerPlugins } = usePlugins();
|
|
@@ -220,7 +217,7 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
|
|
|
220
217
|
webSocketServer.broadcast("startup:progress", { type: "ready" });
|
|
221
218
|
|
|
222
219
|
// Load plugins asynchronously in the background
|
|
223
|
-
(async () => {
|
|
220
|
+
return (async () => {
|
|
224
221
|
const totalStart = Date.now();
|
|
225
222
|
const pluginsToLoad = new Map<string, string>(); // packageName -> version
|
|
226
223
|
|
|
@@ -296,7 +293,10 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
|
|
|
296
293
|
console.error(`[Plugins] Failed to load settings config on startup:`, e);
|
|
297
294
|
}
|
|
298
295
|
|
|
299
|
-
|
|
296
|
+
const pluginList = Array.from(pluginsToLoad.keys())
|
|
297
|
+
.map((p) => ` - ${p}`)
|
|
298
|
+
.join("\n");
|
|
299
|
+
console.log(`\n[Plugins] Loading plugins in background:\n${pluginList}\n`);
|
|
300
300
|
|
|
301
301
|
// Now load all collected plugins in parallel
|
|
302
302
|
const loadPromises = Array.from(pluginsToLoad.entries()).map(async ([packageName, version]) => {
|
|
@@ -307,8 +307,7 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
|
|
|
307
307
|
if (plugin) {
|
|
308
308
|
registerPlugins([plugin]);
|
|
309
309
|
webSocketServer.broadcast("plugin:loaded", { plugin });
|
|
310
|
-
console.
|
|
311
|
-
`[Plugins] Loaded ${packageName}@${version} in ${Date.now() - pluginStart}ms`,
|
|
310
|
+
console.debug(`[Plugins] Loaded ${packageName}@${version} in ${Date.now() - pluginStart}ms`,
|
|
312
311
|
);
|
|
313
312
|
}
|
|
314
313
|
} catch (err) {
|
|
@@ -317,7 +316,7 @@ export const builtInPlugins = async (options: { context: PipelabContext }): Prom
|
|
|
317
316
|
});
|
|
318
317
|
await Promise.all(loadPromises);
|
|
319
318
|
|
|
320
|
-
console.log(
|
|
319
|
+
console.log(`\n[Plugins] All startup plugins loaded in ${Date.now() - totalStart}ms.\n`);
|
|
321
320
|
sendStartupProgress("All plugins loaded.");
|
|
322
321
|
setTimeout(() => {
|
|
323
322
|
webSocketServer.broadcast("startup:progress", { type: "done" });
|
package/src/runner.ts
CHANGED
|
@@ -64,7 +64,7 @@ export async function runPipelineCommand(file: string, options: RunOptions, vers
|
|
|
64
64
|
releaseTag,
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
await registerAllHandlers({ version, context });
|
|
67
|
+
await registerAllHandlers({ version, context, waitForPlugins: true });
|
|
68
68
|
|
|
69
69
|
const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
|
|
70
70
|
await mkdir(cachePath, { recursive: true });
|
package/src/server.ts
CHANGED
package/src/utils/remote.ts
CHANGED
|
@@ -138,8 +138,7 @@ export async function fetchPackage(
|
|
|
138
138
|
const localStart = Date.now();
|
|
139
139
|
const local = await tryResolveMonorepoPackage(packageName);
|
|
140
140
|
if (local) {
|
|
141
|
-
console.
|
|
142
|
-
`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`,
|
|
141
|
+
console.debug(`[Fetcher] ${packageName}: Resolved to local source at ${local.packageDir} (${Date.now() - localStart}ms)`,
|
|
143
142
|
);
|
|
144
143
|
return {
|
|
145
144
|
...local,
|
|
@@ -159,7 +158,7 @@ export async function fetchPackage(
|
|
|
159
158
|
resolvedVersionOrRange = "latest";
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
console.
|
|
161
|
+
console.debug(`[Fetcher] Resolving ${packageName}@${resolvedVersionOrRange || "latest"}...`);
|
|
163
162
|
const resolveStart = Date.now();
|
|
164
163
|
|
|
165
164
|
const online = await isOnline();
|
|
@@ -177,8 +176,7 @@ export async function fetchPackage(
|
|
|
177
176
|
);
|
|
178
177
|
if (fallbackVersion) {
|
|
179
178
|
resolvedVersion = fallbackVersion;
|
|
180
|
-
console.
|
|
181
|
-
`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
|
|
179
|
+
console.debug(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
|
|
182
180
|
);
|
|
183
181
|
} else {
|
|
184
182
|
throw new Error(`Offline and no local fallback version available for ${packageName}`);
|
|
@@ -212,8 +210,7 @@ export async function fetchPackage(
|
|
|
212
210
|
if (
|
|
213
211
|
semver.satisfies(releaseTagVersion, rewrittenRangeForCheck, { includePrerelease: true })
|
|
214
212
|
) {
|
|
215
|
-
console.
|
|
216
|
-
`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) for ${packageName}@${range} because it satisfies the range`,
|
|
213
|
+
console.debug(`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) for ${packageName}@${range} because it satisfies the range`,
|
|
217
214
|
);
|
|
218
215
|
foundVersion = releaseTagVersion;
|
|
219
216
|
}
|
|
@@ -226,8 +223,7 @@ export async function fetchPackage(
|
|
|
226
223
|
if (rewrittenRange !== range) {
|
|
227
224
|
const matched = semver.maxSatisfying(versions, rewrittenRange, { includePrerelease });
|
|
228
225
|
if (matched) {
|
|
229
|
-
console.
|
|
230
|
-
`[Fetcher] Resolved ${packageName}@${range} to ${matched} via rewritten range ${rewrittenRange}`,
|
|
226
|
+
console.debug(`[Fetcher] Resolved ${packageName}@${range} to ${matched} via rewritten range ${rewrittenRange}`,
|
|
231
227
|
);
|
|
232
228
|
foundVersion = matched;
|
|
233
229
|
}
|
|
@@ -244,8 +240,7 @@ export async function fetchPackage(
|
|
|
244
240
|
!foundVersion ||
|
|
245
241
|
(semver.valid(foundVersion) && semver.gte(releaseTagVersion, foundVersion))
|
|
246
242
|
) {
|
|
247
|
-
console.
|
|
248
|
-
`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) instead of "latest" (${foundVersion || "none"}) for ${packageName}`,
|
|
243
|
+
console.debug(`[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) instead of "latest" (${foundVersion || "none"}) for ${packageName}`,
|
|
249
244
|
);
|
|
250
245
|
foundVersion = releaseTagVersion;
|
|
251
246
|
} else if (foundVersion) {
|
|
@@ -262,8 +257,7 @@ export async function fetchPackage(
|
|
|
262
257
|
);
|
|
263
258
|
}
|
|
264
259
|
resolvedVersion = foundVersion;
|
|
265
|
-
console.
|
|
266
|
-
`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`,
|
|
260
|
+
console.debug(`[Fetcher] ${packageName}: Resolved to v${resolvedVersion} via npm (${Date.now() - resolveStart}ms)`,
|
|
267
261
|
);
|
|
268
262
|
} catch (error) {
|
|
269
263
|
console.warn(
|
|
@@ -279,8 +273,7 @@ export async function fetchPackage(
|
|
|
279
273
|
);
|
|
280
274
|
if (fallbackVersion) {
|
|
281
275
|
resolvedVersion = fallbackVersion;
|
|
282
|
-
console.
|
|
283
|
-
`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
|
|
276
|
+
console.debug(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`,
|
|
284
277
|
);
|
|
285
278
|
} else {
|
|
286
279
|
throw error;
|
|
@@ -299,8 +292,7 @@ export async function fetchPackage(
|
|
|
299
292
|
const checkDuration = Date.now() - checkStart;
|
|
300
293
|
|
|
301
294
|
if (isInstalled) {
|
|
302
|
-
console.
|
|
303
|
-
`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`,
|
|
295
|
+
console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Already installed (check took ${checkDuration}ms, fetchPackage took ${Date.now() - start}ms)`,
|
|
304
296
|
);
|
|
305
297
|
return { packageDir, resolvedVersion };
|
|
306
298
|
}
|
|
@@ -308,7 +300,7 @@ export async function fetchPackage(
|
|
|
308
300
|
const lockKey = `package:${packageName}:${resolvedVersion}`;
|
|
309
301
|
return withLock(lockKey, async () => {
|
|
310
302
|
if (!isPackageComplete(packageDir)) {
|
|
311
|
-
console.
|
|
303
|
+
console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Downloading to ${packageDir}...`);
|
|
312
304
|
const downloadStart = Date.now();
|
|
313
305
|
|
|
314
306
|
const tempDir = join(
|
|
@@ -334,13 +326,12 @@ export async function fetchPackage(
|
|
|
334
326
|
await rename(tempDir, packageDir);
|
|
335
327
|
} catch (err: any) {
|
|
336
328
|
if (isPackageComplete(packageDir)) {
|
|
337
|
-
console.
|
|
329
|
+
console.debug(`[Fetcher] Destination ${packageDir} already exists and is valid.`);
|
|
338
330
|
} else {
|
|
339
331
|
throw err;
|
|
340
332
|
}
|
|
341
333
|
}
|
|
342
|
-
console.
|
|
343
|
-
`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`,
|
|
334
|
+
console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Downloaded and extracted in ${Date.now() - downloadStart}ms`,
|
|
344
335
|
);
|
|
345
336
|
} catch (err) {
|
|
346
337
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
@@ -351,20 +342,17 @@ export async function fetchPackage(
|
|
|
351
342
|
// 2. Resolve entry point from package.json for downloaded package
|
|
352
343
|
const entryStart = Date.now();
|
|
353
344
|
const entryPoint = await resolveEntryPoint(packageDir, packageName);
|
|
354
|
-
console.
|
|
355
|
-
`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`,
|
|
345
|
+
console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Resolved entry point in ${Date.now() - entryStart}ms`,
|
|
356
346
|
);
|
|
357
347
|
|
|
358
348
|
if (options?.installDeps) {
|
|
359
349
|
const depsStart = Date.now();
|
|
360
350
|
await installDependencies(packageDir, packageName, options);
|
|
361
|
-
console.
|
|
362
|
-
`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`,
|
|
351
|
+
console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: Installed dependencies in ${Date.now() - depsStart}ms`,
|
|
363
352
|
);
|
|
364
353
|
}
|
|
365
354
|
|
|
366
|
-
console.
|
|
367
|
-
`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`,
|
|
355
|
+
console.debug(`[Fetcher] ${packageName}@${resolvedVersion}: FetchPackage complete in ${Date.now() - start}ms`,
|
|
368
356
|
);
|
|
369
357
|
return { packageDir, resolvedVersion, entryPoint };
|
|
370
358
|
});
|
|
@@ -429,8 +417,7 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
429
417
|
const finalNodePath = join(nodeDir, isWindows ? "node.exe" : "bin/node");
|
|
430
418
|
|
|
431
419
|
if (isNodeJSComplete(finalNodePath)) {
|
|
432
|
-
console.
|
|
433
|
-
`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`,
|
|
420
|
+
console.debug(`[Environment] Node.js check took ${Date.now() - checkStart}ms (found at ${finalNodePath})`,
|
|
434
421
|
);
|
|
435
422
|
return finalNodePath;
|
|
436
423
|
}
|
|
@@ -453,7 +440,7 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
453
440
|
console.log(`Downloading Node.js from ${downloadUrl}...`);
|
|
454
441
|
const dlStart = Date.now();
|
|
455
442
|
await downloadFile(downloadUrl, archivePath);
|
|
456
|
-
console.
|
|
443
|
+
console.debug(`[Environment] Node.js download took ${Date.now() - dlStart}ms`);
|
|
457
444
|
|
|
458
445
|
sendStartupProgress(`Extracting Node.js v${version}...`);
|
|
459
446
|
console.log(`Extracting Node.js to ${tempDir}...`);
|
|
@@ -496,18 +483,18 @@ export async function ensureNodeJS(context: PipelabContext, version = DEFAULT_NO
|
|
|
496
483
|
await rename(tempNodeDir, nodeDir);
|
|
497
484
|
} catch (err: any) {
|
|
498
485
|
if (isNodeJSComplete(finalNodePath)) {
|
|
499
|
-
console.
|
|
486
|
+
console.debug(`[Fetcher] Node.js directory already exists and is valid.`);
|
|
500
487
|
} else {
|
|
501
488
|
throw err;
|
|
502
489
|
}
|
|
503
490
|
}
|
|
504
|
-
console.
|
|
491
|
+
console.debug(`[Environment] Node.js extraction took ${Date.now() - extStart}ms`);
|
|
505
492
|
} finally {
|
|
506
493
|
await rm(tempNodeDir, { recursive: true, force: true }).catch(() => {});
|
|
507
494
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
508
495
|
}
|
|
509
496
|
|
|
510
|
-
console.
|
|
497
|
+
console.debug(`[Environment] Node.js set up complete in ${Date.now() - checkStart}ms`);
|
|
511
498
|
return finalNodePath;
|
|
512
499
|
});
|
|
513
500
|
}
|
|
@@ -521,8 +508,7 @@ export async function ensurePNPM(context: PipelabContext, version = DEFAULT_PNPM
|
|
|
521
508
|
const pnpmPath = join(pnpmDir, "bin", "pnpm.cjs");
|
|
522
509
|
|
|
523
510
|
if (existsSync(pnpmPath)) {
|
|
524
|
-
console.
|
|
525
|
-
`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`,
|
|
511
|
+
console.debug(`[Environment] PNPM check took ${Date.now() - checkStart}ms (found at ${pnpmPath})`,
|
|
526
512
|
);
|
|
527
513
|
return pnpmPath;
|
|
528
514
|
}
|
|
@@ -534,7 +520,7 @@ export async function ensurePNPM(context: PipelabContext, version = DEFAULT_PNPM
|
|
|
534
520
|
const { packageDir } = await fetchPackage("pnpm", version, {
|
|
535
521
|
context,
|
|
536
522
|
});
|
|
537
|
-
console.
|
|
523
|
+
console.debug(`[Environment] PNPM set up complete in ${Date.now() - checkStart}ms`);
|
|
538
524
|
return join(packageDir, "bin", "pnpm.cjs");
|
|
539
525
|
});
|
|
540
526
|
}
|
|
@@ -544,23 +530,22 @@ async function installDependencies(packageDir: string, packageName: string, opti
|
|
|
544
530
|
const nodeModulesPath = join(packageDir, "node_modules");
|
|
545
531
|
|
|
546
532
|
if (isDependenciesInstalledSync(packageDir)) {
|
|
547
|
-
console.
|
|
533
|
+
console.debug(`[Fetcher] ${packageName}: Dependencies already installed, skipping.`);
|
|
548
534
|
return;
|
|
549
535
|
}
|
|
550
536
|
|
|
551
537
|
try {
|
|
552
|
-
console.
|
|
538
|
+
console.debug(`[Fetcher] ${packageName}: Ensuring dependencies are installed...`);
|
|
553
539
|
const pnpmStart = Date.now();
|
|
554
540
|
const { all } = await runPnpm(packageDir, {
|
|
555
541
|
signal: options.signal,
|
|
556
542
|
context: options.context,
|
|
557
543
|
});
|
|
558
|
-
console.
|
|
544
|
+
console.debug(`[Fetcher] ${packageName}: pnpm install command took ${Date.now() - pnpmStart}ms`);
|
|
559
545
|
|
|
560
|
-
if (all) console.
|
|
546
|
+
if (all) console.debug(`[Fetcher] ${packageName}: Installation trace:\n${all}`);
|
|
561
547
|
|
|
562
|
-
console.
|
|
563
|
-
`[Fetcher] ${packageName}: Dependencies installed successfully (total installDependencies took ${Date.now() - start}ms).`,
|
|
548
|
+
console.debug(`[Fetcher] ${packageName}: Dependencies installed successfully (total installDependencies took ${Date.now() - start}ms).`,
|
|
564
549
|
);
|
|
565
550
|
} catch (err: any) {
|
|
566
551
|
console.error(
|
package/src/utils.ts
CHANGED
|
@@ -129,25 +129,26 @@ export const executeGraphWithHistory = async ({
|
|
|
129
129
|
|
|
130
130
|
// Ensure all plugins used in the graph are downloaded and registered
|
|
131
131
|
const { registerPlugins, plugins: registeredPlugins } = usePlugins();
|
|
132
|
+
// JIT loading is intentionally removed here so plugins must be loaded at app startup.
|
|
133
|
+
// Fail-fast if any required plugin is not loaded
|
|
132
134
|
const pluginIds = new Set(
|
|
133
135
|
graph.map((node: any) => node.origin?.pluginId).filter(Boolean),
|
|
134
136
|
) as Set<string>;
|
|
135
137
|
|
|
138
|
+
const missingPlugins: string[] = [];
|
|
136
139
|
for (const pluginId of pluginIds) {
|
|
137
140
|
const isRegistered = registeredPlugins.value.some((p) => p.id === pluginId);
|
|
138
141
|
if (!isRegistered) {
|
|
139
|
-
|
|
140
|
-
const pluginDefinition = await loadPipelabPlugin(pluginId, { context: ctx });
|
|
141
|
-
|
|
142
|
-
if (pluginDefinition) {
|
|
143
|
-
registerPlugins([pluginDefinition]);
|
|
144
|
-
logger().info(`[Runner] Plugin "${pluginId}" loaded and registered successfully`);
|
|
145
|
-
} else {
|
|
146
|
-
logger().error(`[Runner] Failed to load or register plugin "${pluginId}"`);
|
|
147
|
-
}
|
|
142
|
+
missingPlugins.push(pluginId);
|
|
148
143
|
}
|
|
149
144
|
}
|
|
150
145
|
|
|
146
|
+
if (missingPlugins.length > 0) {
|
|
147
|
+
const errorMsg = `Fail-fast: The following required plugins are not loaded: ${missingPlugins.join(", ")}. Please ensure they are installed and enabled in settings.`;
|
|
148
|
+
logger().error(`[Runner] ${errorMsg}`);
|
|
149
|
+
throw new Error(errorMsg);
|
|
150
|
+
}
|
|
151
|
+
|
|
151
152
|
logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
|
|
152
153
|
const settingsFile = await setupSettingsConfigFile(ctx);
|
|
153
154
|
const config = await settingsFile.getConfig();
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import pacote from "pacote";
|
|
2
|
-
import semver from "semver";
|
|
3
|
-
import { fetchLatestDesktopRelease } from "../src/utils/github.js";
|
|
4
|
-
|
|
5
|
-
const PACKAGES = [
|
|
6
|
-
// Apps & Core
|
|
7
|
-
"@pipelab/cli",
|
|
8
|
-
"@pipelab/ui",
|
|
9
|
-
"@pipelab/shared",
|
|
10
|
-
"@pipelab/core-node",
|
|
11
|
-
"@pipelab/constants",
|
|
12
|
-
"@pipelab/migration",
|
|
13
|
-
|
|
14
|
-
// Plugins
|
|
15
|
-
"@pipelab/plugin-construct",
|
|
16
|
-
"@pipelab/plugin-core",
|
|
17
|
-
"@pipelab/plugin-discord",
|
|
18
|
-
"@pipelab/plugin-electron",
|
|
19
|
-
"@pipelab/plugin-filesystem",
|
|
20
|
-
"@pipelab/plugin-itch",
|
|
21
|
-
"@pipelab/plugin-minify",
|
|
22
|
-
"@pipelab/plugin-netlify",
|
|
23
|
-
"@pipelab/plugin-nvpatch",
|
|
24
|
-
"@pipelab/plugin-poki",
|
|
25
|
-
"@pipelab/plugin-steam",
|
|
26
|
-
"@pipelab/plugin-system",
|
|
27
|
-
"@pipelab/plugin-tauri",
|
|
28
|
-
|
|
29
|
-
// Assets
|
|
30
|
-
"@pipelab/asset-discord",
|
|
31
|
-
"@pipelab/asset-electron",
|
|
32
|
-
"@pipelab/asset-netlify",
|
|
33
|
-
"@pipelab/asset-tauri",
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
async function simulate() {
|
|
37
|
-
console.log("==========================================");
|
|
38
|
-
console.log(" PIPELAB UPDATE FLOW SIMULATOR ");
|
|
39
|
-
console.log("==========================================\n");
|
|
40
|
-
|
|
41
|
-
// 1. Simulate Desktop GitHub Release Update
|
|
42
|
-
console.log("--- 1. Simulating Desktop Application (GitHub Releases) ---");
|
|
43
|
-
try {
|
|
44
|
-
const stableApp = await fetchLatestDesktopRelease({ allowPrerelease: false });
|
|
45
|
-
const betaApp = await fetchLatestDesktopRelease({ allowPrerelease: true });
|
|
46
|
-
|
|
47
|
-
console.log(
|
|
48
|
-
`[STABLE] Resolved version: ${stableApp ? stableApp.tag_name : "None (Stable users protected)"}`,
|
|
49
|
-
);
|
|
50
|
-
console.log(`[BETA] Resolved version: ${betaApp ? betaApp.tag_name : "None"}`);
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.error("Failed to fetch desktop releases from GitHub:", error);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log("\n--- 2. Simulating NPM Packages (CLI, UI, Plugins, Assets) ---");
|
|
56
|
-
console.log("Fetching data from NPM registry. Please wait...\n");
|
|
57
|
-
|
|
58
|
-
const results: Array<{
|
|
59
|
-
package: string;
|
|
60
|
-
stable: string;
|
|
61
|
-
beta: string;
|
|
62
|
-
notes: string;
|
|
63
|
-
}> = [];
|
|
64
|
-
|
|
65
|
-
for (const pkg of PACKAGES) {
|
|
66
|
-
try {
|
|
67
|
-
const packument = await pacote.packument(pkg);
|
|
68
|
-
const stableVersion = packument["dist-tags"]?.latest || "N/A";
|
|
69
|
-
const betaVersion = packument["dist-tags"]?.beta || "N/A";
|
|
70
|
-
|
|
71
|
-
let notes = "";
|
|
72
|
-
if (stableVersion === "N/A" && betaVersion === "N/A") {
|
|
73
|
-
notes = "⚠️ Package not published on NPM";
|
|
74
|
-
} else if (betaVersion !== "N/A" && stableVersion !== "N/A") {
|
|
75
|
-
if (betaVersion === stableVersion) {
|
|
76
|
-
notes = "Stable and Beta are in sync";
|
|
77
|
-
} else {
|
|
78
|
-
// Attempt semver comparison
|
|
79
|
-
const cleanStable = semver.coerce(stableVersion);
|
|
80
|
-
const cleanBeta = semver.coerce(betaVersion);
|
|
81
|
-
if (cleanStable && cleanBeta) {
|
|
82
|
-
const comp = semver.compare(stableVersion, betaVersion);
|
|
83
|
-
if (comp < 0) {
|
|
84
|
-
notes = "🚀 Beta is ahead";
|
|
85
|
-
} else if (comp > 0) {
|
|
86
|
-
notes = "Stable is ahead";
|
|
87
|
-
} else {
|
|
88
|
-
notes = "Stable and Beta are in sync (coerced)";
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
notes = "Different versions (non-semver)";
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
} else if (betaVersion !== "N/A" && stableVersion === "N/A") {
|
|
95
|
-
notes = "Only Beta version exists";
|
|
96
|
-
} else if (stableVersion !== "N/A" && betaVersion === "N/A") {
|
|
97
|
-
notes = "No Beta tag published";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
results.push({
|
|
101
|
-
package: pkg,
|
|
102
|
-
stable: stableVersion,
|
|
103
|
-
beta: betaVersion,
|
|
104
|
-
notes,
|
|
105
|
-
});
|
|
106
|
-
} catch (error: any) {
|
|
107
|
-
results.push({
|
|
108
|
-
package: pkg,
|
|
109
|
-
stable: "ERROR",
|
|
110
|
-
beta: "ERROR",
|
|
111
|
-
notes: `Failed to fetch: ${error.message}`,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
console.table(results);
|
|
117
|
-
console.log("\n==========================================\n");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
simulate().catch(console.error);
|