@pipelab/core-node 1.0.0-beta.17 → 1.0.0-beta.19

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/src/runner.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  import { executeGraphWithHistory } from "./utils";
2
2
  import { setupConfigFile } from "./config";
3
- import { isDev, PipelabContext } from "./context";
3
+ import { isDev, PipelabContext, CacheFolder } from "./context";
4
4
  import { readFile, access, writeFile, mkdir } from "node:fs/promises";
5
5
  import { resolve, isAbsolute, join, dirname } from "node:path";
6
6
  import { savedFileMigrator } from "@pipelab/shared";
7
- import type { AppConfig } from "@pipelab/shared";
8
- import { registerMigrationHandlers } from "./migrations";
9
7
  import { registerAllHandlers } from "./handlers/index";
10
8
 
11
9
  export interface RunOptions {
@@ -65,11 +63,8 @@ export async function runPipelineCommand(file: string, options: RunOptions, vers
65
63
  });
66
64
 
67
65
  await registerAllHandlers({ version, context });
68
- registerMigrationHandlers(context);
69
66
 
70
- const settings = await setupConfigFile<AppConfig>("settings", { context });
71
- const config = await settings.getConfig();
72
- const cachePath = join(context.userDataPath, "cache");
67
+ const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
73
68
  await mkdir(cachePath, { recursive: true });
74
69
 
75
70
  const abortController = new AbortController();
package/src/server.ts CHANGED
@@ -9,11 +9,9 @@ import {
9
9
  import { getUiDevServerMissingWarning, uiDevPort } from "@pipelab/constants";
10
10
  import { existsSync } from "node:fs";
11
11
  import { readFile } from "node:fs/promises";
12
- import { join } from "node:path";
13
12
  import http from "http";
14
13
  // @ts-expect-error serve-handler has no type definitions
15
14
  import handler from "serve-handler";
16
- import { registerMigrationHandlers } from "./migrations";
17
15
 
18
16
  export interface ServeOptions {
19
17
  port: string | number;
@@ -116,6 +114,7 @@ export async function serveCommand(options: ServeOptions, version: string, _dirn
116
114
 
117
115
  return handler(request, response, {
118
116
  public: rawAssetFolder,
117
+ rewrites: [{ source: "/**", destination: "/index.html" }],
119
118
  });
120
119
  });
121
120
 
@@ -134,7 +133,6 @@ export async function serveCommand(options: ServeOptions, version: string, _dirn
134
133
  version,
135
134
  context,
136
135
  });
137
- registerMigrationHandlers(context);
138
136
 
139
137
  sendStartupReady();
140
138
 
@@ -29,7 +29,7 @@ describe("GitHub Release Updates API", () => {
29
29
  expect(releases).toEqual([]);
30
30
  expect(fetchSpy).toHaveBeenCalledWith(
31
31
  expect.stringContaining("matching-refs/tags/%40pipelab%2Fapp"),
32
- expect.any(Object)
32
+ expect.any(Object),
33
33
  );
34
34
  });
35
35
 
@@ -153,7 +153,7 @@ describe("GitHub Release Updates API", () => {
153
153
  expect(releases[0].tag_name).toBe("@pipelab/app@1.2.3");
154
154
  expect(fetchSpy).toHaveBeenCalledWith(
155
155
  expect.stringContaining("releases/tags/%40pipelab%2Fapp%401.2.3"),
156
- expect.any(Object)
156
+ expect.any(Object),
157
157
  );
158
158
  });
159
159
 
@@ -189,9 +189,7 @@ describe("GitHub Release Updates API", () => {
189
189
  // Mock matching refs
190
190
  fetchSpy.mockResolvedValueOnce({
191
191
  ok: true,
192
- json: async () => [
193
- { ref: "refs/tags/@pipelab/app@3.0.0" },
194
- ],
192
+ json: async () => [{ ref: "refs/tags/@pipelab/app@3.0.0" }],
195
193
  } as any);
196
194
 
197
195
  // Mock release lookup
@@ -93,13 +93,16 @@ describe("remote utilities & offline mode", () => {
93
93
 
94
94
  test("fetchPipelabPlugin maps latest to releaseTag for official plugins", async () => {
95
95
  vi.advanceTimersByTime(30000); // Bypass 10s caching
96
- const context = new PipelabContext({ userDataPath: "/tmp/pipelab-test-remote", releaseTag: "beta" });
96
+ const context = new PipelabContext({
97
+ userDataPath: "/tmp/pipelab-test-remote",
98
+ releaseTag: "beta",
99
+ });
97
100
  const pluginName = "@pipelab/plugin-poki";
98
101
  const packageBaseDir = context.getPackagesPath(pluginName);
99
102
  const cachedVersionDir = path.join(packageBaseDir, "1.0.0-beta.15");
100
103
 
101
104
  vi.spyOn(dns, "lookup").mockResolvedValue({ address: "1.2.3.4", family: 4 } as any);
102
-
105
+
103
106
  vi.mocked(pacote.packument).mockResolvedValue({
104
107
  name: pluginName,
105
108
  versions: {
@@ -128,13 +131,16 @@ describe("remote utilities & offline mode", () => {
128
131
 
129
132
  test("fetchPipelabPlugin maps latest to releaseTag for custom plugins and falls back to latest if tag is missing", async () => {
130
133
  vi.advanceTimersByTime(40000); // Bypass 10s caching
131
- const context = new PipelabContext({ userDataPath: "/tmp/pipelab-test-remote-custom", releaseTag: "beta" });
134
+ const context = new PipelabContext({
135
+ userDataPath: "/tmp/pipelab-test-remote-custom",
136
+ releaseTag: "beta",
137
+ });
132
138
  const pluginName = "custom-cool-plugin";
133
139
  const packageBaseDir = context.getPackagesPath(pluginName);
134
140
  const cachedVersionDir = path.join(packageBaseDir, "2.0.0");
135
141
 
136
142
  vi.spyOn(dns, "lookup").mockResolvedValue({ address: "1.2.3.4", family: 4 } as any);
137
-
143
+
138
144
  vi.mocked(pacote.packument).mockResolvedValue({
139
145
  name: pluginName,
140
146
  versions: {
@@ -163,13 +169,16 @@ describe("remote utilities & offline mode", () => {
163
169
 
164
170
  test("fetchPipelabPlugin maps latest to releaseTag for custom plugins and falls back to latest if beta is stale", async () => {
165
171
  vi.advanceTimersByTime(50000); // Bypass 10s caching
166
- const context = new PipelabContext({ userDataPath: "/tmp/pipelab-test-remote-stale", releaseTag: "beta" });
172
+ const context = new PipelabContext({
173
+ userDataPath: "/tmp/pipelab-test-remote-stale",
174
+ releaseTag: "beta",
175
+ });
167
176
  const pluginName = "stale-beta-plugin";
168
177
  const packageBaseDir = context.getPackagesPath(pluginName);
169
178
  const cachedVersionDir = path.join(packageBaseDir, "2.0.0");
170
179
 
171
180
  vi.spyOn(dns, "lookup").mockResolvedValue({ address: "1.2.3.4", family: 4 } as any);
172
-
181
+
173
182
  vi.mocked(pacote.packument).mockResolvedValue({
174
183
  name: pluginName,
175
184
  versions: {
@@ -14,7 +14,7 @@ import { existsSync, constants, statSync, readdirSync } from "node:fs";
14
14
  import dns from "node:dns/promises";
15
15
  import pacote from "pacote";
16
16
  import semver from "semver";
17
- import { isDev, projectRoot, PipelabContext } from "../context";
17
+ import { isDev, projectRoot, PipelabContext, CacheFolder } from "../context";
18
18
  import { execa } from "execa";
19
19
  import { sendStartupProgress } from "../server";
20
20
  import { downloadFile, extractZip, extractTarGz } from "./fs-extras";
@@ -163,7 +163,7 @@ export async function fetchPackage(
163
163
  } else {
164
164
  try {
165
165
  // 1. Resolve version/range using npm with session-wide memoization and disk cache
166
- const cachePath = join(ctx.userDataPath, "cache", "pacote");
166
+ const cachePath = ctx.getCachePath(CacheFolder.Pacote);
167
167
  let packumentPromise = packumentRequests.get(packageName);
168
168
  if (!packumentPromise) {
169
169
  packumentPromise = pacote.packument(packageName, { cache: cachePath });
@@ -183,7 +183,10 @@ export async function fetchPackage(
183
183
  if (range === "latest" && ctx.releaseTag && ctx.releaseTag !== "latest") {
184
184
  const releaseTagVersion = packument["dist-tags"]?.[ctx.releaseTag];
185
185
  if (releaseTagVersion && semver.valid(releaseTagVersion)) {
186
- if (!foundVersion || (semver.valid(foundVersion) && semver.gte(releaseTagVersion, foundVersion))) {
186
+ if (
187
+ !foundVersion ||
188
+ (semver.valid(foundVersion) && semver.gte(releaseTagVersion, foundVersion))
189
+ ) {
187
190
  console.log(
188
191
  `[Fetcher] Using release tag "${ctx.releaseTag}" (${releaseTagVersion}) instead of "latest" (${foundVersion || "none"}) for ${packageName}`,
189
192
  );
@@ -227,7 +230,7 @@ export async function fetchPackage(
227
230
  }
228
231
  }
229
232
 
230
- const cachePath = join(ctx.userDataPath, "cache", "pacote");
233
+ const cachePath = ctx.getCachePath(CacheFolder.Pacote);
231
234
  const packageDir = join(baseDir, resolvedVersion);
232
235
 
233
236
  const checkStart = Date.now();
@@ -351,7 +354,7 @@ export async function runPnpm(
351
354
  ...process.env,
352
355
  NODE_ENV: "production",
353
356
  PATH: nodePath ? `${dirname(nodePath)}${delimiter}${process.env.PATH}` : process.env.PATH,
354
- PNPM_HOME: join(ctx.userDataPath, "pnpm"),
357
+ PNPM_HOME: ctx.getPnpmPath(),
355
358
  PNPM_ONLY_ALLOW_TRUSTED_DEPENDENCIES: "false",
356
359
  ...extraEnv,
357
360
  },
@@ -12,7 +12,8 @@ export class JsonFileStorage {
12
12
  private logger = useLogger().logger;
13
13
 
14
14
  constructor(fileName: string, context: PipelabContext) {
15
- this.filePath = path.join(context.userDataPath, fileName);
15
+ this.filePath = context.getConfigPath(fileName);
16
+
16
17
  const dir = path.dirname(this.filePath);
17
18
  if (!fs.existsSync(dir)) {
18
19
  try {
package/src/utils.ts CHANGED
@@ -21,7 +21,7 @@ import { BuildHistoryStorage } from "./handlers/build-history";
21
21
  import { ensure, extractTarGz, extractZip, zipFolder } from "./utils/fs-extras";
22
22
  import { fetchPipelabAsset } from "./utils/remote";
23
23
  import { loadPipelabPlugin } from "./plugins-registry";
24
- import { setupConfigFile } from "./config";
24
+ import { setupSettingsConfigFile } from "./config";
25
25
 
26
26
  export const getFinalPlugins = () => {
27
27
  const { plugins } = usePlugins();
@@ -102,6 +102,7 @@ export const executeGraphWithHistory = async ({
102
102
  projectName,
103
103
  projectPath,
104
104
  pipelineId,
105
+ cachePath,
105
106
  startTime,
106
107
  status: "running",
107
108
  logs: [],
@@ -148,7 +149,7 @@ export const executeGraphWithHistory = async ({
148
149
  }
149
150
 
150
151
  logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
151
- const settingsFile = await setupConfigFile<AppConfig>("settings", { context: ctx });
152
+ const settingsFile = await setupSettingsConfigFile(ctx);
152
153
  const config = await settingsFile.getConfig();
153
154
  const shouldCleanup = true;
154
155
 
@@ -247,18 +248,17 @@ export const executeGraphWithHistory = async ({
247
248
 
248
249
  throw error;
249
250
  } finally {
250
- // Apply retention policy regardless of outcome
251
- if (!shouldDisableHistory) {
252
- // Don't await, let it run in the background
253
- buildHistoryStorage.applyRetentionPolicy();
254
- }
255
-
256
251
  if (shouldCleanup) {
257
252
  try {
258
253
  await rm(sandboxPath, { recursive: true, force: true });
259
254
  } catch (e) {
260
255
  console.warn(`Failed to cleanup sandbox at ${sandboxPath}:`, e);
261
256
  }
257
+ try {
258
+ await rm(cachePath, { recursive: true, force: true });
259
+ } catch (e) {
260
+ console.warn(`Failed to cleanup cache at ${cachePath}:`, e);
261
+ }
262
262
  }
263
263
  }
264
264
  };
@@ -1,2 +0,0 @@
1
- import { setupConfigFile } from "./index.mjs";
2
- export { setupConfigFile };
package/src/migrations.ts DELETED
@@ -1,60 +0,0 @@
1
- import { useAPI } from "./ipc-core";
2
- import { PipelabContext } from "./context";
3
- import { setupConfigFile, getMigrator } from "./config";
4
- import { useLogger } from "@pipelab/shared";
5
-
6
- /**
7
- * Registers migration handlers for the CLI/Standalone server.
8
- * This overrides the default handlers from @pipelab/core-node to include
9
- * pipeline and file repo migrations directly in the backend.
10
- */
11
- export function registerMigrationHandlers(context: PipelabContext) {
12
- const { handle } = useAPI();
13
- const { logger } = useLogger();
14
-
15
- handle("config:load", async (_, { send, value }) => {
16
- const { config: name } = value;
17
- logger().info("[CLI Migration] config:load", name);
18
-
19
- try {
20
- // Determine which migrator to use
21
- const migrator = getMigrator(name);
22
-
23
- if (!migrator) {
24
- throw new Error(
25
- `No migrator found for configuration: ${name}. All files loaded via config:load MUST have a migration schema.`,
26
- );
27
- }
28
-
29
- // We use a custom loading logic that applies the migrator
30
- // Since setupConfigFile in core-node expects the migrator to be in the configRegistry,
31
- // and we want to keep migrations in the CLI package, we'll manually apply them here.
32
-
33
- const manager = await setupConfigFile(name, { context, migrator });
34
- const json = await manager.getConfig();
35
-
36
- send({
37
- type: "end",
38
- data: {
39
- type: "success",
40
- result: {
41
- result: json,
42
- },
43
- },
44
- });
45
- } catch (e) {
46
- logger().error(`[CLI Migration] config:load error for ${name}:`, e);
47
- send({
48
- type: "end",
49
- data: {
50
- type: "error",
51
- ipcError: e instanceof Error ? e.message : `Unable to load config ${name}`,
52
- },
53
- });
54
- }
55
- });
56
-
57
- // We also need to override config:save if we want to ensure consistency,
58
- // although mostly we just care about migrations on load.
59
- // The default config:save from core-node will work fine as it uses the same setupConfigFile logic.
60
- }