@pipelab/shared 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 +1 -1
- package/CHANGELOG.md +82 -0
- package/dist/index.d.mts +467 -153
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +440 -188
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -4
- package/src/apis.ts +40 -13
- package/src/build-history.ts +2 -0
- package/src/config/connections-definition.ts +3 -0
- package/src/config/migrators.ts +227 -85
- package/src/config/settings-definition.ts +2 -0
- package/src/config.schema.ts +71 -2
- package/src/graph.ts +2 -70
- package/src/i18n/de_DE.json +5 -1
- package/src/i18n/en_US.json +18 -3
- package/src/i18n/es_ES.json +5 -1
- package/src/i18n/fr_FR.json +5 -1
- package/src/i18n/pt_BR.json +5 -1
- package/src/i18n/zh_CN.json +5 -1
- package/src/index.ts +19 -0
- package/src/ipc.types.ts +2 -0
- package/src/model.test.ts +313 -1
- package/src/model.ts +54 -43
- package/src/plugins/definitions.ts +25 -55
- package/src/plugins.ts +10 -1
- package/src/utils.ts +26 -0
- package/tsconfig.json +1 -3
package/src/config.schema.ts
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
number,
|
|
9
9
|
array,
|
|
10
10
|
GenericSchema,
|
|
11
|
+
optional,
|
|
12
|
+
looseObject,
|
|
11
13
|
} from "valibot";
|
|
12
14
|
|
|
13
15
|
export const createVersionSchema = <T extends GenericSchema<any, any>>(schema: T) => schema;
|
|
@@ -135,6 +137,72 @@ export const AppSettingsValidatorV7 = object({
|
|
|
135
137
|
}),
|
|
136
138
|
});
|
|
137
139
|
|
|
140
|
+
export const AppSettingsValidatorV8 = object({
|
|
141
|
+
theme: union([literal("light"), literal("dark")]),
|
|
142
|
+
version: literal("8.0.0"),
|
|
143
|
+
locale: union([
|
|
144
|
+
literal("en-US"),
|
|
145
|
+
literal("fr-FR"),
|
|
146
|
+
literal("pt-BR"),
|
|
147
|
+
literal("zh-CN"),
|
|
148
|
+
literal("es-ES"),
|
|
149
|
+
literal("de-DE"),
|
|
150
|
+
]),
|
|
151
|
+
tours: object({
|
|
152
|
+
dashboard: object({
|
|
153
|
+
step: number(),
|
|
154
|
+
completed: boolean(),
|
|
155
|
+
}),
|
|
156
|
+
editor: object({
|
|
157
|
+
step: number(),
|
|
158
|
+
completed: boolean(),
|
|
159
|
+
}),
|
|
160
|
+
}),
|
|
161
|
+
autosave: boolean(),
|
|
162
|
+
agents: array(
|
|
163
|
+
object({
|
|
164
|
+
id: string(),
|
|
165
|
+
name: string(),
|
|
166
|
+
url: string(),
|
|
167
|
+
}),
|
|
168
|
+
),
|
|
169
|
+
buildHistory: object({
|
|
170
|
+
retentionPolicy: object({
|
|
171
|
+
enabled: boolean(),
|
|
172
|
+
maxEntries: number(), // Maximum number of entries per pipeline
|
|
173
|
+
maxAge: number(), // Maximum age of entries in days
|
|
174
|
+
}),
|
|
175
|
+
}),
|
|
176
|
+
// Metadata list of plugins the user has enabled (official + community).
|
|
177
|
+
// No binaries are stored here — versions are resolved JIT at node-add time.
|
|
178
|
+
plugins: array(
|
|
179
|
+
object({
|
|
180
|
+
name: string(),
|
|
181
|
+
enabled: boolean(),
|
|
182
|
+
description: string(),
|
|
183
|
+
}),
|
|
184
|
+
),
|
|
185
|
+
isInternalMigrationBannerClosed: optional(boolean(), false),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
export const ConnectionValidator = looseObject({
|
|
189
|
+
id: string(),
|
|
190
|
+
pluginName: string(),
|
|
191
|
+
name: string(),
|
|
192
|
+
createdAt: string(),
|
|
193
|
+
isDefault: boolean(),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
export const ConnectionsValidatorV1 = object({
|
|
197
|
+
version: literal("1.0.0"),
|
|
198
|
+
connections: array(ConnectionValidator),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export type Connection = InferInput<typeof ConnectionValidator>;
|
|
202
|
+
export type ConnectionsConfigV1 = InferInput<typeof ConnectionsValidatorV1>;
|
|
203
|
+
export type ConnectionsConfig = ConnectionsConfigV1;
|
|
204
|
+
export const ConnectionsValidator = ConnectionsValidatorV1;
|
|
205
|
+
|
|
138
206
|
export type AppConfigV1 = InferInput<typeof AppSettingsValidatorV1>;
|
|
139
207
|
export type AppConfigV2 = InferInput<typeof AppSettingsValidatorV2>;
|
|
140
208
|
export type AppConfigV3 = InferInput<typeof AppSettingsValidatorV3>;
|
|
@@ -142,6 +210,7 @@ export type AppConfigV4 = InferInput<typeof AppSettingsValidatorV4>;
|
|
|
142
210
|
export type AppConfigV5 = InferInput<typeof AppSettingsValidatorV5>;
|
|
143
211
|
export type AppConfigV6 = InferInput<typeof AppSettingsValidatorV6>;
|
|
144
212
|
export type AppConfigV7 = InferInput<typeof AppSettingsValidatorV7>;
|
|
213
|
+
export type AppConfigV8 = InferInput<typeof AppSettingsValidatorV8>;
|
|
145
214
|
|
|
146
|
-
export type AppConfig =
|
|
147
|
-
export const AppSettingsValidator =
|
|
215
|
+
export type AppConfig = AppConfigV8;
|
|
216
|
+
export const AppSettingsValidator = AppSettingsValidatorV8;
|
package/src/graph.ts
CHANGED
|
@@ -42,7 +42,7 @@ export const processGraph = async (options: {
|
|
|
42
42
|
node: Block,
|
|
43
43
|
params: Record<string, string>,
|
|
44
44
|
steps: Steps,
|
|
45
|
-
) => Promise<End<"
|
|
45
|
+
) => Promise<End<"action:execute">>;
|
|
46
46
|
onNodeEnter: (node: Block) => void;
|
|
47
47
|
onNodeExit: (node: Block) => void;
|
|
48
48
|
abortSignal?: AbortSignal;
|
|
@@ -68,47 +68,7 @@ export const processGraph = async (options: {
|
|
|
68
68
|
options.definitions,
|
|
69
69
|
);
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
options.onNodeEnter(rawNode)
|
|
73
|
-
|
|
74
|
-
const newParams = await makeResolvedParams({
|
|
75
|
-
params: rawNode.params,
|
|
76
|
-
variables: options.variables,
|
|
77
|
-
steps: options.steps,
|
|
78
|
-
context: options.context
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
const result = await options.onExecuteItem(node, newParams, options.steps) as End<'condition:execute'>
|
|
82
|
-
|
|
83
|
-
if ('result' in result) {
|
|
84
|
-
logger().error(result.result)
|
|
85
|
-
options.onNodeExit(rawNode)
|
|
86
|
-
throw new Error('Condition error')
|
|
87
|
-
} else {
|
|
88
|
-
const { value, outputs } = result
|
|
89
|
-
if (!options.steps[rawNode.uid]) {
|
|
90
|
-
options.steps[rawNode.uid] = {
|
|
91
|
-
outputs: {}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
options.steps[rawNode.uid].outputs = outputs
|
|
95
|
-
|
|
96
|
-
if (value === true) {
|
|
97
|
-
await processGraph({
|
|
98
|
-
graph: rawNode.branchTrue,
|
|
99
|
-
...options,
|
|
100
|
-
abortSignal: options.abortSignal
|
|
101
|
-
})
|
|
102
|
-
} else {
|
|
103
|
-
await processGraph({
|
|
104
|
-
graph: rawNode.branchFalse,
|
|
105
|
-
...options,
|
|
106
|
-
abortSignal: options.abortSignal
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
options.onNodeExit(rawNode)
|
|
111
|
-
} else */ if (rawNode.type === "action") {
|
|
71
|
+
if (rawNode.type === "action") {
|
|
112
72
|
if (rawNode.disabled === true) {
|
|
113
73
|
console.warn(
|
|
114
74
|
`Node ${rawNode.uid} (${rawNode.origin.pluginId}::${rawNode.origin.nodeId}) is disabled`,
|
|
@@ -163,34 +123,6 @@ export const processGraph = async (options: {
|
|
|
163
123
|
options.steps[rawNode.uid].outputs = result.result.outputs;
|
|
164
124
|
}
|
|
165
125
|
options.onNodeExit(rawNode);
|
|
166
|
-
} else if (rawNode.type === "loop") {
|
|
167
|
-
options.onNodeEnter(rawNode);
|
|
168
|
-
|
|
169
|
-
// const context = {}
|
|
170
|
-
|
|
171
|
-
// const arrayToLoopOn = await evaluate(rawNode.params.value, context)
|
|
172
|
-
|
|
173
|
-
// element is the value of the element at loopindex
|
|
174
|
-
// let loopindex = 0
|
|
175
|
-
// for (const _element of arrayToLoopOn) {
|
|
176
|
-
// await processGraph(rawNode.children, definitions, variables, steps, {
|
|
177
|
-
// ...context,
|
|
178
|
-
// loopindex
|
|
179
|
-
// })
|
|
180
|
-
|
|
181
|
-
// loopindex += 1
|
|
182
|
-
// }
|
|
183
|
-
|
|
184
|
-
// continue after loop
|
|
185
|
-
|
|
186
|
-
// TODO: process loop
|
|
187
|
-
// const result = await api.execute('node:execute', {
|
|
188
|
-
// nodeId: rawNode.origin.nodeId,
|
|
189
|
-
// pluginId: rawNode.origin.pluginId,
|
|
190
|
-
// params: rawNode.params
|
|
191
|
-
// })
|
|
192
|
-
// console.log('result', result)
|
|
193
|
-
options.onNodeExit(rawNode);
|
|
194
126
|
} else if (rawNode.type === "comment") {
|
|
195
127
|
// pass
|
|
196
128
|
} else if (rawNode.type === "event") {
|
package/src/i18n/de_DE.json
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
"storage": "Storage",
|
|
16
16
|
"integrations": "Integrations",
|
|
17
17
|
"advanced": "Advanced",
|
|
18
|
-
"billing": "Billing"
|
|
18
|
+
"billing": "Billing",
|
|
19
|
+
"plugins": "Plugins",
|
|
20
|
+
"core-plugins": "Core-Plugins",
|
|
21
|
+
"community-plugins": "Community-Plugins",
|
|
22
|
+
"versions": "Versionen"
|
|
19
23
|
},
|
|
20
24
|
"clearTempFolders": "Automatically clean up temporary files",
|
|
21
25
|
"clearTempFoldersDescription": "Wenn aktiviert, werden alle temporären Dateien, die während der Pipeline-Ausführung erstellt wurden, automatisch gelöscht, nachdem die Pipeline abgeschlossen ist. Dies hilft, Speicherplatz auf der Festplatte zu sparen, aber möglicherweise müssen Sie selbst Artefakte kopieren, um sie zu bewahren.",
|
package/src/i18n/en_US.json
CHANGED
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"storage": "Storage",
|
|
18
18
|
"integrations": "Integrations",
|
|
19
19
|
"advanced": "Advanced",
|
|
20
|
-
"billing": "Billing"
|
|
20
|
+
"billing": "Billing",
|
|
21
|
+
"plugins": "Plugins",
|
|
22
|
+
"core-plugins": "Core Plugins",
|
|
23
|
+
"community-plugins": "Community Plugins",
|
|
24
|
+
"versions": "Versions"
|
|
21
25
|
},
|
|
22
26
|
"pipeline-cache-folder": "Pipeline Cache Folder:",
|
|
23
27
|
"enter-or-browse-for-a-folder": "Enter or browse for a folder",
|
|
@@ -83,7 +87,14 @@
|
|
|
83
87
|
"your-project-has-be-saved-successfully": "Your project has be saved successfully",
|
|
84
88
|
"view-history": "History",
|
|
85
89
|
"add-plugin": "Add plugin",
|
|
86
|
-
"display-advanced-nodes": "Display advanced nodes"
|
|
90
|
+
"display-advanced-nodes": "Display advanced nodes",
|
|
91
|
+
"project-settings": "Pipeline Settings",
|
|
92
|
+
"jit-loading-title": "Installing Required Plugins",
|
|
93
|
+
"jit-loading-subtitle": "Please wait while Pipelab fetches and prepares the necessary nodes for this pipeline.",
|
|
94
|
+
"jit-loading-status": "Downloading and building plugin packages…",
|
|
95
|
+
"validation-failed": "Validation Failed",
|
|
96
|
+
"validation-plugin-disabled-or-missing": "Plugin \"{pluginId}\" is disabled or not installed (required by \"{nodeName}\").",
|
|
97
|
+
"validation-missing-param": "Block \"{nodeName}\": missing required parameter \"{paramName}\"."
|
|
87
98
|
},
|
|
88
99
|
"home": {
|
|
89
100
|
"invalid-preset": "Invalid preset",
|
|
@@ -129,7 +140,11 @@
|
|
|
129
140
|
"confirm-migration-message": "Are you sure you want to migrate this pipeline to internal storage? This will create a copy in the internal storage managed by Pipelab.",
|
|
130
141
|
"migrate-pipeline": "Migrate Pipeline",
|
|
131
142
|
"migration-success": "Pipeline migrated successfully",
|
|
132
|
-
"migrate-to-internal": "Migrate to Internal"
|
|
143
|
+
"migrate-to-internal": "Migrate to Internal",
|
|
144
|
+
"only-internal-supported-notice": "Pipelab now stores pipelines internally. External pipeline files are deprecated. You can still import external files as internal pipelines.",
|
|
145
|
+
"import-pipeline": "Import Pipeline",
|
|
146
|
+
"import-success": "Pipeline imported successfully",
|
|
147
|
+
"failed-to-read-file": "Failed to read the selected file"
|
|
133
148
|
},
|
|
134
149
|
"scenarios": {
|
|
135
150
|
"scenarios": "Scenarios"
|
package/src/i18n/es_ES.json
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
"storage": "Storage",
|
|
16
16
|
"integrations": "Integrations",
|
|
17
17
|
"advanced": "Advanced",
|
|
18
|
-
"billing": "Billing"
|
|
18
|
+
"billing": "Billing",
|
|
19
|
+
"plugins": "Plugins",
|
|
20
|
+
"core-plugins": "Complementos principales",
|
|
21
|
+
"community-plugins": "Complementos de la comunidad",
|
|
22
|
+
"versions": "Versiones"
|
|
19
23
|
},
|
|
20
24
|
"clearTempFolders": "Automatically clean up temporary files",
|
|
21
25
|
"clearTempFoldersDescription": "Cuando está habilitado, todos los archivos temporales creados durante la ejecución del pipeline se eliminarán automáticamente después de que el pipeline se complete. Esto ayuda a ahorrar espacio en el disco, pero puede que necesite copiar los artefactos usted mismo para preservarlos.",
|
package/src/i18n/fr_FR.json
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
"storage": "Stockage",
|
|
16
16
|
"integrations": "Intégrations",
|
|
17
17
|
"advanced": "Avancé",
|
|
18
|
-
"billing": "Facturation"
|
|
18
|
+
"billing": "Facturation",
|
|
19
|
+
"plugins": "Plugins",
|
|
20
|
+
"core-plugins": "Plugins de base",
|
|
21
|
+
"community-plugins": "Plugins de la communauté",
|
|
22
|
+
"versions": "Versions"
|
|
19
23
|
},
|
|
20
24
|
"clearTempFolders": "Nettoyer automatiquement les fichiers temporaires",
|
|
21
25
|
"clearTempFoldersDescription": "Lorsque cette option est activée, tous les fichiers temporaires créés lors de l'exécution du pipeline seront automatiquement supprimés une fois le pipeline terminé. Cela permet d'économiser de l'espace disque, mais vous devrez peut-être copier vous-même des artefacts pour les préserver.",
|
package/src/i18n/pt_BR.json
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
"storage": "Armazenagem",
|
|
16
16
|
"integrations": "Integrações",
|
|
17
17
|
"advanced": "Avançado",
|
|
18
|
-
"billing": "Pagamento"
|
|
18
|
+
"billing": "Pagamento",
|
|
19
|
+
"plugins": "Plugins",
|
|
20
|
+
"core-plugins": "Plugins nativos",
|
|
21
|
+
"community-plugins": "Plugins da comunidade",
|
|
22
|
+
"versions": "Versões"
|
|
19
23
|
},
|
|
20
24
|
"clearTempFolders": "Automatically clean up temporary files",
|
|
21
25
|
"clearTempFoldersDescription": "When enabled, all temporary files created during pipeline execution will be automatically deleted after the pipeline completes. This helps save disk space but you may need to copy artifacts yourself to preserve them.",
|
package/src/i18n/zh_CN.json
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
"storage": "Storage",
|
|
16
16
|
"integrations": "Integrations",
|
|
17
17
|
"advanced": "Advanced",
|
|
18
|
-
"billing": "Billing"
|
|
18
|
+
"billing": "Billing",
|
|
19
|
+
"plugins": "Plugins",
|
|
20
|
+
"core-plugins": "核心插件",
|
|
21
|
+
"community-plugins": "社区插件",
|
|
22
|
+
"versions": "版本"
|
|
19
23
|
},
|
|
20
24
|
"clearTempFolders": "Automatically clean up temporary files",
|
|
21
25
|
"clearTempFoldersDescription": "When enabled, all temporary files created during pipeline execution will be automatically deleted after the pipeline completes. This helps save disk space but you may need to copy artifacts yourself to preserve them.",
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
defaultFileRepo as _defaultFileRepo,
|
|
7
7
|
savedFileMigrator as _savedFileMigrator,
|
|
8
8
|
configRegistry as _configRegistry,
|
|
9
|
+
normalizePipelineConfig as _normalizePipelineConfig,
|
|
10
|
+
connectionsMigrator as _connectionsMigrator,
|
|
11
|
+
defaultConnections as _defaultConnections,
|
|
9
12
|
} from "./config/migrators";
|
|
10
13
|
|
|
11
14
|
export const appSettingsMigrator = _appSettingsMigrator;
|
|
@@ -14,9 +17,24 @@ export const fileRepoMigrations = _fileRepoMigrations;
|
|
|
14
17
|
export const defaultFileRepo = _defaultFileRepo;
|
|
15
18
|
export const savedFileMigrator = _savedFileMigrator;
|
|
16
19
|
export const configRegistry = _configRegistry;
|
|
20
|
+
export const normalizePipelineConfig = _normalizePipelineConfig;
|
|
21
|
+
export const connectionsMigrator = _connectionsMigrator;
|
|
22
|
+
export const defaultConnections = _defaultConnections;
|
|
17
23
|
|
|
18
24
|
// 2. Types
|
|
19
25
|
export type { Migrator } from "./config/migrators";
|
|
26
|
+
export type { IpcDefinition } from "./apis";
|
|
27
|
+
export type { IpcDefinition as RendererIpcDefinition } from "./ipc.types";
|
|
28
|
+
export type { RequestId } from "./apis";
|
|
29
|
+
export type { RequestId as RendererRequestId } from "./ipc.types";
|
|
30
|
+
export type { NodeId } from "./model";
|
|
31
|
+
export type { FileRepo, FileRepoV1, FileRepoV2 } from "./config/projects-definition";
|
|
32
|
+
export {
|
|
33
|
+
FileRepoValidator,
|
|
34
|
+
FileRepoValidatorV1,
|
|
35
|
+
FileRepoValidatorV2,
|
|
36
|
+
FileRepoProjectValidatorV2,
|
|
37
|
+
} from "./config/projects-definition";
|
|
20
38
|
|
|
21
39
|
// 3. Core Library exports (Systematic restoration)
|
|
22
40
|
export * from "./apis";
|
|
@@ -45,6 +63,7 @@ export * from "./websocket.types";
|
|
|
45
63
|
// 4. Configuration Sub-packages
|
|
46
64
|
export * from "./config/projects-definition"; // <-- RE-ADDED
|
|
47
65
|
export * from "./config/settings-definition"; // <-- RE-ADDED
|
|
66
|
+
export * from "./config/connections-definition";
|
|
48
67
|
export * from "./config/projects-types";
|
|
49
68
|
|
|
50
69
|
// NOTE: We avoid "export * from './config'" to prevent nested re-export circles.
|
package/src/ipc.types.ts
CHANGED
package/src/model.test.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { SavedFileV1, SavedFileV2, SavedFileV3, SavedFileV4, SavedFileV5 } from "./model";
|
|
3
|
+
import {
|
|
4
|
+
savedFileMigrator,
|
|
5
|
+
normalizePipelineConfig,
|
|
6
|
+
appSettingsMigrator,
|
|
7
|
+
fileRepoMigrations,
|
|
8
|
+
connectionsMigrator,
|
|
9
|
+
} from "./config/migrators";
|
|
10
|
+
import { AppConfigV6, AppConfigV8 } from "./config.schema";
|
|
3
11
|
|
|
4
12
|
describe("model", () => {
|
|
5
13
|
it("should migrate 1.0.0 to 2.0.0", async () => {
|
|
@@ -211,4 +219,308 @@ describe("model", () => {
|
|
|
211
219
|
version: "3.0.0",
|
|
212
220
|
} satisfies SavedFileV3);
|
|
213
221
|
});
|
|
222
|
+
|
|
223
|
+
it("should migrate 3.0.0 to 4.0.0", async () => {
|
|
224
|
+
const v3: SavedFileV3 = {
|
|
225
|
+
version: "3.0.0",
|
|
226
|
+
canvas: {
|
|
227
|
+
blocks: [],
|
|
228
|
+
triggers: [],
|
|
229
|
+
},
|
|
230
|
+
description: "desc",
|
|
231
|
+
name: "name",
|
|
232
|
+
variables: [],
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const v4 = await savedFileMigrator.migrate(v3, {
|
|
236
|
+
debug: true,
|
|
237
|
+
target: "4.0.0",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(v4).toStrictEqual({
|
|
241
|
+
version: "4.0.0",
|
|
242
|
+
type: "default",
|
|
243
|
+
canvas: {
|
|
244
|
+
blocks: [],
|
|
245
|
+
triggers: [],
|
|
246
|
+
},
|
|
247
|
+
description: "desc",
|
|
248
|
+
name: "name",
|
|
249
|
+
variables: [],
|
|
250
|
+
} satisfies SavedFileV4);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should migrate 4.0.0 to 5.0.0", async () => {
|
|
254
|
+
const v4: SavedFileV4 = {
|
|
255
|
+
version: "4.0.0",
|
|
256
|
+
type: "default",
|
|
257
|
+
canvas: {
|
|
258
|
+
blocks: [
|
|
259
|
+
{
|
|
260
|
+
uid: "b1",
|
|
261
|
+
type: "action",
|
|
262
|
+
origin: {
|
|
263
|
+
pluginId: "electron",
|
|
264
|
+
nodeId: "open",
|
|
265
|
+
},
|
|
266
|
+
params: {},
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
uid: "b2",
|
|
270
|
+
type: "action",
|
|
271
|
+
origin: {
|
|
272
|
+
pluginId: "dicord",
|
|
273
|
+
nodeId: "send",
|
|
274
|
+
},
|
|
275
|
+
params: {},
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
triggers: [],
|
|
279
|
+
},
|
|
280
|
+
description: "desc",
|
|
281
|
+
name: "name",
|
|
282
|
+
variables: [],
|
|
283
|
+
plugins: {
|
|
284
|
+
discord: "1.0.0",
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const v5 = await savedFileMigrator.migrate(v4, {
|
|
289
|
+
debug: true,
|
|
290
|
+
target: "5.0.0",
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(v5).toStrictEqual({
|
|
294
|
+
version: "5.0.0",
|
|
295
|
+
canvas: {
|
|
296
|
+
blocks: [
|
|
297
|
+
{
|
|
298
|
+
uid: "b1",
|
|
299
|
+
type: "action",
|
|
300
|
+
origin: {
|
|
301
|
+
pluginId: "@pipelab/plugin-electron",
|
|
302
|
+
nodeId: "open",
|
|
303
|
+
// "electron" wasn't in the old plugins map, so version falls back to "latest"
|
|
304
|
+
version: "latest",
|
|
305
|
+
},
|
|
306
|
+
params: {},
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
uid: "b2",
|
|
310
|
+
type: "action",
|
|
311
|
+
origin: {
|
|
312
|
+
pluginId: "@pipelab/plugin-discord",
|
|
313
|
+
nodeId: "send",
|
|
314
|
+
// "dicord" mapped to "@pipelab/plugin-discord" which had version "1.0.0" in the old map
|
|
315
|
+
version: "1.0.0",
|
|
316
|
+
},
|
|
317
|
+
params: {},
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
triggers: [],
|
|
321
|
+
},
|
|
322
|
+
description: "desc",
|
|
323
|
+
name: "name",
|
|
324
|
+
variables: [],
|
|
325
|
+
// plugins top-level map is dropped in V5 for default pipelines
|
|
326
|
+
} satisfies SavedFileV5);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("should migrate AppConfigV6 to AppConfigV8", async () => {
|
|
330
|
+
const v6: AppConfigV6 = {
|
|
331
|
+
version: "6.0.0",
|
|
332
|
+
theme: "dark",
|
|
333
|
+
cacheFolder: "/some/path",
|
|
334
|
+
clearTemporaryFoldersOnPipelineEnd: true,
|
|
335
|
+
locale: "fr-FR",
|
|
336
|
+
tours: {
|
|
337
|
+
dashboard: { step: 1, completed: true },
|
|
338
|
+
editor: { step: 2, completed: false },
|
|
339
|
+
},
|
|
340
|
+
autosave: false,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const v8 = await appSettingsMigrator.migrate(v6, { target: "8.0.0" });
|
|
344
|
+
|
|
345
|
+
expect(v8).toStrictEqual({
|
|
346
|
+
version: "8.0.0",
|
|
347
|
+
theme: "dark",
|
|
348
|
+
locale: "fr-FR",
|
|
349
|
+
tours: {
|
|
350
|
+
dashboard: { step: 1, completed: true },
|
|
351
|
+
editor: { step: 2, completed: false },
|
|
352
|
+
},
|
|
353
|
+
autosave: false,
|
|
354
|
+
agents: [],
|
|
355
|
+
buildHistory: {
|
|
356
|
+
retentionPolicy: {
|
|
357
|
+
enabled: false,
|
|
358
|
+
maxEntries: 50,
|
|
359
|
+
maxAge: 30,
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
plugins: expect.any(Array),
|
|
363
|
+
isInternalMigrationBannerClosed: false,
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe("normalizePipelineConfig", () => {
|
|
368
|
+
it("should normalize typo and legacy plugin IDs in block origins", () => {
|
|
369
|
+
const config = {
|
|
370
|
+
version: "5.0.0",
|
|
371
|
+
type: "default",
|
|
372
|
+
canvas: {
|
|
373
|
+
blocks: [
|
|
374
|
+
{
|
|
375
|
+
uid: "b1",
|
|
376
|
+
type: "action",
|
|
377
|
+
origin: {
|
|
378
|
+
pluginId: "dicord",
|
|
379
|
+
nodeId: "send",
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const changed = normalizePipelineConfig(config);
|
|
387
|
+
expect(changed).toBe(true);
|
|
388
|
+
expect(config.canvas.blocks[0].origin.pluginId).toBe("@pipelab/plugin-discord");
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe("fileRepoMigrations", () => {
|
|
393
|
+
it("should migrate FileRepoV1 (data record) to FileRepoV2 (pipelines array)", async () => {
|
|
394
|
+
const v1 = {
|
|
395
|
+
version: "1.0.0" as const,
|
|
396
|
+
data: {
|
|
397
|
+
"pipeline-1": {
|
|
398
|
+
project: "main",
|
|
399
|
+
type: "internal" as const,
|
|
400
|
+
configName: "pipeline-1",
|
|
401
|
+
lastModified: "2026-06-04",
|
|
402
|
+
},
|
|
403
|
+
"pipeline-2": {
|
|
404
|
+
project: "main",
|
|
405
|
+
type: "external" as const,
|
|
406
|
+
path: "/path/to/pipeline-2.json",
|
|
407
|
+
lastModified: "2026-06-04",
|
|
408
|
+
summary: {
|
|
409
|
+
plugins: ["steam"],
|
|
410
|
+
name: "Test Pipeline 2",
|
|
411
|
+
description: "Legacy external pipeline",
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const v2 = await fileRepoMigrations.migrate(v1, { target: "2.0.0" });
|
|
418
|
+
|
|
419
|
+
expect(v2).toStrictEqual({
|
|
420
|
+
version: "2.0.0",
|
|
421
|
+
data: v1.data,
|
|
422
|
+
projects: [
|
|
423
|
+
{
|
|
424
|
+
id: "main",
|
|
425
|
+
name: "Default project",
|
|
426
|
+
description: "The initial default project",
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
pipelines: [
|
|
430
|
+
{
|
|
431
|
+
id: "pipeline-1",
|
|
432
|
+
project: "main",
|
|
433
|
+
type: "internal",
|
|
434
|
+
configName: "pipeline-1",
|
|
435
|
+
lastModified: "2026-06-04",
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
id: "pipeline-2",
|
|
439
|
+
project: "main",
|
|
440
|
+
type: "external",
|
|
441
|
+
path: "/path/to/pipeline-2.json",
|
|
442
|
+
lastModified: "2026-06-04",
|
|
443
|
+
summary: {
|
|
444
|
+
plugins: ["steam"],
|
|
445
|
+
name: "Test Pipeline 2",
|
|
446
|
+
description: "Legacy external pipeline",
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("should fallback to default value for corrupted config", async () => {
|
|
454
|
+
const corrupted: any = {
|
|
455
|
+
version: "1.0.0",
|
|
456
|
+
data: null,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const result = await fileRepoMigrations.migrate(corrupted, { target: "2.0.0" });
|
|
460
|
+
expect(result.version).toBe("2.0.0");
|
|
461
|
+
expect(result.projects).toHaveLength(1);
|
|
462
|
+
expect(result.pipelines).toEqual([]);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe("connectionsMigrator", () => {
|
|
467
|
+
it("should initialize default connections", async () => {
|
|
468
|
+
const result = await connectionsMigrator.migrate(undefined, { target: "1.0.0" });
|
|
469
|
+
expect(result).toStrictEqual({
|
|
470
|
+
version: "1.0.0",
|
|
471
|
+
connections: [],
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
describe("savedFileMigrator - edge cases", () => {
|
|
477
|
+
it("should normalize unmapped legacy plugin names and typos during migration to 5.0.0", async () => {
|
|
478
|
+
const v4: SavedFileV4 = {
|
|
479
|
+
version: "4.0.0",
|
|
480
|
+
type: "default",
|
|
481
|
+
canvas: {
|
|
482
|
+
blocks: [
|
|
483
|
+
{
|
|
484
|
+
uid: "b1",
|
|
485
|
+
type: "action",
|
|
486
|
+
origin: {
|
|
487
|
+
pluginId: "filesystem",
|
|
488
|
+
nodeId: "copy",
|
|
489
|
+
},
|
|
490
|
+
params: {},
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
uid: "b2",
|
|
494
|
+
type: "action",
|
|
495
|
+
origin: {
|
|
496
|
+
pluginId: "dicord",
|
|
497
|
+
nodeId: "send",
|
|
498
|
+
},
|
|
499
|
+
params: {},
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
uid: "b3",
|
|
503
|
+
type: "action",
|
|
504
|
+
origin: {
|
|
505
|
+
pluginId: "custom-cool",
|
|
506
|
+
nodeId: "run",
|
|
507
|
+
},
|
|
508
|
+
params: {},
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
triggers: [],
|
|
512
|
+
},
|
|
513
|
+
description: "Edge case plugin names test",
|
|
514
|
+
name: "Edge Case",
|
|
515
|
+
variables: [],
|
|
516
|
+
plugins: {},
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const v5 = await savedFileMigrator.migrate(v4, { target: "5.0.0" });
|
|
520
|
+
|
|
521
|
+
expect(v5.canvas.blocks[0].origin.pluginId).toBe("@pipelab/plugin-filesystem");
|
|
522
|
+
expect(v5.canvas.blocks[1].origin.pluginId).toBe("@pipelab/plugin-discord");
|
|
523
|
+
expect(v5.canvas.blocks[2].origin.pluginId).toBe("custom-cool");
|
|
524
|
+
});
|
|
525
|
+
});
|
|
214
526
|
});
|