@pipelab/shared 2.0.1-beta.20
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/.turbo/turbo-build.log +41 -0
- package/.turbo/turbo-lint.log +136 -0
- package/CHANGELOG.md +167 -0
- package/LICENSE +110 -0
- package/LICENSE.md +110 -0
- package/README.md +3 -0
- package/dist/index.d.mts +3603 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +43763 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -0
- package/src/apis.ts +191 -0
- package/src/build-history.ts +127 -0
- package/src/config/migrators.ts +308 -0
- package/src/config/projects-definition.ts +25 -0
- package/src/config/projects-types.ts +25 -0
- package/src/config/projects.ts +1 -0
- package/src/config/settings-definition.ts +21 -0
- package/src/config/settings.ts +21 -0
- package/src/config.schema.ts +149 -0
- package/src/database.types.ts +95 -0
- package/src/errors.ts +11 -0
- package/src/evaluator.ts +58 -0
- package/src/fmt.ts +5 -0
- package/src/graph.ts +207 -0
- package/src/i18n/de_DE.json +86 -0
- package/src/i18n/en_US.json +147 -0
- package/src/i18n/es_ES.json +86 -0
- package/src/i18n/fr_FR.json +89 -0
- package/src/i18n/pt_BR.json +86 -0
- package/src/i18n/zh_CN.json +86 -0
- package/src/i18n-utils.ts +10 -0
- package/src/index.ts +51 -0
- package/src/ipc.types.ts +73 -0
- package/src/logger.ts +20 -0
- package/src/migrations/model.ts +1 -0
- package/src/migrations/projects.ts +1 -0
- package/src/migrations/settings.ts +1 -0
- package/src/model.test.ts +214 -0
- package/src/model.ts +233 -0
- package/src/plugins/definitions.ts +472 -0
- package/src/plugins.ts +20 -0
- package/src/quickjs.ts +98 -0
- package/src/save-location.ts +42 -0
- package/src/subscription-errors.ts +87 -0
- package/src/supabase.ts +28 -0
- package/src/tests/helpers.ts +5 -0
- package/src/types.ts +3 -0
- package/src/utils.ts +3 -0
- package/src/validation.ts +16 -0
- package/src/variables.ts +27 -0
- package/src/wasm.d.ts +9 -0
- package/src/websocket.types.ts +186 -0
- package/tsconfig.json +13 -0
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipelab/shared",
|
|
3
|
+
"version": "2.0.1-beta.20",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Shared logic and types for the Pipelab ecosystem",
|
|
6
|
+
"license": "FSL-1.1-MIT",
|
|
7
|
+
"author": "CynToolkit",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/CynToolkit/pipelab.git",
|
|
11
|
+
"directory": "packages/shared"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./dist/index.cjs",
|
|
15
|
+
"module": "./dist/index.mjs",
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"import": "./dist/index.mjs",
|
|
21
|
+
"require": "./dist/index.cjs",
|
|
22
|
+
"default": "./dist/index.mjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@jitl/quickjs-singlefile-mjs-release-sync": "0.31.0",
|
|
30
|
+
"@jitl/quickjs-wasmfile-release-sync": "0.31.0",
|
|
31
|
+
"@supabase/supabase-js": "2.99.3",
|
|
32
|
+
"@types/node": "^24.12.2",
|
|
33
|
+
"klona": "2.0.6",
|
|
34
|
+
"nanoid": "5.0.7",
|
|
35
|
+
"quickjs-emscripten": "0.31.0",
|
|
36
|
+
"quickjs-emscripten-sync": "1.6.0",
|
|
37
|
+
"tslog": "4.9.3",
|
|
38
|
+
"type-fest": "4.26.1",
|
|
39
|
+
"valibot": "0.42.1",
|
|
40
|
+
"@pipelab/migration": "1.0.1-beta.19"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"esbuild-plugin-wasm": "1.1.0",
|
|
44
|
+
"tsdown": "0.21.2",
|
|
45
|
+
"typescript": "5.9.3",
|
|
46
|
+
"@pipelab/tsconfig": "1.0.1-beta.19"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsdown",
|
|
50
|
+
"format": "oxfmt .",
|
|
51
|
+
"lint": "oxlint ."
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/apis.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { RendererPluginDefinition } from "./plugins/definitions";
|
|
2
|
+
import { User, UserResponse } from "@supabase/supabase-js";
|
|
3
|
+
import type { Tagged } from "type-fest";
|
|
4
|
+
import { PresetResult, Steps } from "./model";
|
|
5
|
+
import { AppConfig } from "./config.schema";
|
|
6
|
+
import { Agent } from "./websocket.types";
|
|
7
|
+
import {
|
|
8
|
+
BuildHistoryEntry,
|
|
9
|
+
BuildHistoryQuery,
|
|
10
|
+
BuildHistoryResponse,
|
|
11
|
+
BuildHistoryConfig,
|
|
12
|
+
RetentionPolicy,
|
|
13
|
+
} from "./build-history";
|
|
14
|
+
|
|
15
|
+
type Event<TYPE extends string, DATA> =
|
|
16
|
+
| { type: TYPE; data: DATA }
|
|
17
|
+
| { type: "log"; data: { decorator: string; message: unknown[]; time: number } };
|
|
18
|
+
type EndEvent<DATA> = {
|
|
19
|
+
type: "end";
|
|
20
|
+
data:
|
|
21
|
+
| {
|
|
22
|
+
type: "success";
|
|
23
|
+
result: DATA;
|
|
24
|
+
}
|
|
25
|
+
| {
|
|
26
|
+
type: "error";
|
|
27
|
+
ipcError: string;
|
|
28
|
+
code?: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type Presets = Record<string, PresetResult>;
|
|
33
|
+
|
|
34
|
+
export type IpcDefinition = {
|
|
35
|
+
"fs:read": [
|
|
36
|
+
// input
|
|
37
|
+
{ path: string },
|
|
38
|
+
EndEvent<{ content: string }>,
|
|
39
|
+
];
|
|
40
|
+
"fs:remove": [
|
|
41
|
+
// input
|
|
42
|
+
{ path: string },
|
|
43
|
+
EndEvent<boolean>,
|
|
44
|
+
];
|
|
45
|
+
"fs:write": [
|
|
46
|
+
// input
|
|
47
|
+
{
|
|
48
|
+
path: string;
|
|
49
|
+
content: string;
|
|
50
|
+
},
|
|
51
|
+
EndEvent<{ ok: boolean }>,
|
|
52
|
+
];
|
|
53
|
+
"fs:rm": [
|
|
54
|
+
// input
|
|
55
|
+
{
|
|
56
|
+
path: string;
|
|
57
|
+
recursive: boolean;
|
|
58
|
+
force: boolean;
|
|
59
|
+
},
|
|
60
|
+
EndEvent<{ ok: boolean }>,
|
|
61
|
+
];
|
|
62
|
+
"fs:listDirectory": [
|
|
63
|
+
// input
|
|
64
|
+
{ path: string },
|
|
65
|
+
EndEvent<{
|
|
66
|
+
files: {
|
|
67
|
+
name: string;
|
|
68
|
+
isDirectory: boolean;
|
|
69
|
+
isSymbolicLink: boolean;
|
|
70
|
+
size: number;
|
|
71
|
+
mtime: number;
|
|
72
|
+
}[];
|
|
73
|
+
}>,
|
|
74
|
+
];
|
|
75
|
+
"fs:getHomeDirectory": [void, EndEvent<{ path: string }>];
|
|
76
|
+
"dialog:showOpenDialog": [
|
|
77
|
+
// input
|
|
78
|
+
Electron.OpenDialogOptions,
|
|
79
|
+
EndEvent<{ canceled: boolean; filePaths: string[] }>,
|
|
80
|
+
];
|
|
81
|
+
"dialog:showSaveDialog": [
|
|
82
|
+
// input
|
|
83
|
+
Electron.SaveDialogOptions,
|
|
84
|
+
EndEvent<{ canceled: boolean; filePath: string | undefined }>,
|
|
85
|
+
];
|
|
86
|
+
"nodes:get": [void, EndEvent<{ nodes: RendererPluginDefinition[] }>];
|
|
87
|
+
"presets:get": [void, EndEvent<Presets>];
|
|
88
|
+
"action:execute": [
|
|
89
|
+
{
|
|
90
|
+
pluginId: string;
|
|
91
|
+
nodeId: string;
|
|
92
|
+
params: any;
|
|
93
|
+
steps: Steps;
|
|
94
|
+
},
|
|
95
|
+
(
|
|
96
|
+
| Event<"progress", unknown>
|
|
97
|
+
| Event<"progress", unknown>
|
|
98
|
+
| EndEvent<{ outputs: Record<string, unknown>; tmp: string }>
|
|
99
|
+
),
|
|
100
|
+
];
|
|
101
|
+
"condition:execute": [
|
|
102
|
+
{
|
|
103
|
+
pluginId: string;
|
|
104
|
+
nodeId: string;
|
|
105
|
+
params: any;
|
|
106
|
+
steps: Steps;
|
|
107
|
+
},
|
|
108
|
+
(
|
|
109
|
+
| Event<"progress", unknown>
|
|
110
|
+
| Event<"progress", unknown>
|
|
111
|
+
| EndEvent<{ outputs: Record<string, unknown>; value: boolean }>
|
|
112
|
+
),
|
|
113
|
+
];
|
|
114
|
+
"constants:get": [void, EndEvent<{ result: { userData: string } }>];
|
|
115
|
+
|
|
116
|
+
"config:load": [{ config: string }, EndEvent<{ result: any }>];
|
|
117
|
+
"config:save": [{ data: any; config: string }, EndEvent<{ result: "ok" }>];
|
|
118
|
+
"config:reset": [{ config: string; key: string }, EndEvent<{ result: "ok" }>];
|
|
119
|
+
"action:cancel": [void, EndEvent<{ result: "ok" | "ko" }>];
|
|
120
|
+
|
|
121
|
+
// Build History APIs
|
|
122
|
+
"build-history:save": [{ entry: BuildHistoryEntry }, EndEvent<{ result: "ok" | "ko" }>];
|
|
123
|
+
"build-history:get": [
|
|
124
|
+
{ id: string; pipelineId?: string },
|
|
125
|
+
EndEvent<{ entry?: BuildHistoryEntry }>,
|
|
126
|
+
];
|
|
127
|
+
"build-history:get-all": [{ query?: BuildHistoryQuery }, EndEvent<BuildHistoryResponse>];
|
|
128
|
+
"build-history:update": [
|
|
129
|
+
{ id: string; updates: Partial<BuildHistoryEntry>; pipelineId?: string },
|
|
130
|
+
EndEvent<{ result: "ok" | "ko" }>,
|
|
131
|
+
];
|
|
132
|
+
"build-history:delete": [{ id: string; pipelineId?: string }, EndEvent<{ result: "ok" | "ko" }>];
|
|
133
|
+
"build-history:clear": [void, EndEvent<{ result: "ok" | "ko" }>];
|
|
134
|
+
"build-history:get-storage-info": [
|
|
135
|
+
void,
|
|
136
|
+
EndEvent<{
|
|
137
|
+
totalEntries: number;
|
|
138
|
+
totalSize: number;
|
|
139
|
+
oldestEntry?: number;
|
|
140
|
+
newestEntry?: number;
|
|
141
|
+
}>,
|
|
142
|
+
];
|
|
143
|
+
"build-history:configure": [
|
|
144
|
+
{ config: Partial<BuildHistoryConfig> },
|
|
145
|
+
EndEvent<{ result: "ok" | "ko" }>,
|
|
146
|
+
];
|
|
147
|
+
"agents:get": [void, EndEvent<{ agents: Agent[] }>];
|
|
148
|
+
"graph:execute": [
|
|
149
|
+
{
|
|
150
|
+
graph: any[];
|
|
151
|
+
variables: any[];
|
|
152
|
+
pipelineId?: string;
|
|
153
|
+
projectId?: string;
|
|
154
|
+
projectName?: string;
|
|
155
|
+
projectPath?: string;
|
|
156
|
+
},
|
|
157
|
+
(
|
|
158
|
+
| { type: "node-enter"; data: { nodeUid: string; nodeName: string } }
|
|
159
|
+
| { type: "node-exit"; data: { nodeUid: string; nodeName: string } }
|
|
160
|
+
| { type: "node-log"; data: { nodeUid: string; logData: any } }
|
|
161
|
+
| EndEvent<{ result: any; buildId: string }>
|
|
162
|
+
),
|
|
163
|
+
];
|
|
164
|
+
"auth:getUser": [void, EndEvent<{ user: User | null }>];
|
|
165
|
+
"auth:signInWithPassword": [{ email: string; password: string }, EndEvent<UserResponse>];
|
|
166
|
+
"auth:signUp": [{ email: string; password: string }, EndEvent<UserResponse>];
|
|
167
|
+
"auth:signOut": [void, EndEvent<void>];
|
|
168
|
+
"auth:resetPasswordForEmail": [{ email: string }, EndEvent<{ error: any | null }>];
|
|
169
|
+
"auth:invoke": [
|
|
170
|
+
{ name: string; options?: any },
|
|
171
|
+
EndEvent<{ data: any | null; error: any | null }>,
|
|
172
|
+
];
|
|
173
|
+
"agent:version:get": [void, EndEvent<{ version: string }>];
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type Channels = keyof IpcDefinition;
|
|
177
|
+
|
|
178
|
+
export const ShellChannels: Channels[] = ["dialog:showOpenDialog", "dialog:showSaveDialog"];
|
|
179
|
+
|
|
180
|
+
export type Data<KEY extends Channels> = IpcDefinition[KEY][0];
|
|
181
|
+
export type Events<KEY extends Channels> = IpcDefinition[KEY][1];
|
|
182
|
+
export type End<KEY extends Channels> = Extract<IpcDefinition[KEY][1], { type: "end" }>["data"];
|
|
183
|
+
|
|
184
|
+
export type IpcMessage = {
|
|
185
|
+
// the channel to communicate
|
|
186
|
+
requestId: RequestId;
|
|
187
|
+
data: any;
|
|
188
|
+
};
|
|
189
|
+
export type RequestId = Tagged<string, "request-id">;
|
|
190
|
+
|
|
191
|
+
// type Output = End<'fs:openFolder'>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Build History Storage Types and Interfaces
|
|
2
|
+
|
|
3
|
+
export interface ExecutionStep {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
status: "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
7
|
+
startTime: number;
|
|
8
|
+
endTime?: number;
|
|
9
|
+
duration?: number;
|
|
10
|
+
logs: LogEntry[];
|
|
11
|
+
error?: ExecutionError;
|
|
12
|
+
output?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ExecutionError {
|
|
16
|
+
message: string;
|
|
17
|
+
stack?: string;
|
|
18
|
+
code?: string;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LogEntry {
|
|
23
|
+
id: string;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
level: "debug" | "info" | "warn" | "error";
|
|
26
|
+
message: string;
|
|
27
|
+
source?: string;
|
|
28
|
+
data?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface BuildHistoryEntry {
|
|
32
|
+
id: string;
|
|
33
|
+
pipelineId: string;
|
|
34
|
+
projectName: string;
|
|
35
|
+
projectPath: string;
|
|
36
|
+
status: "running" | "completed" | "failed" | "cancelled";
|
|
37
|
+
startTime: number;
|
|
38
|
+
endTime?: number;
|
|
39
|
+
duration?: number;
|
|
40
|
+
steps: ExecutionStep[];
|
|
41
|
+
totalSteps: number;
|
|
42
|
+
completedSteps: number;
|
|
43
|
+
failedSteps: number;
|
|
44
|
+
cancelledSteps: number;
|
|
45
|
+
logs: LogEntry[];
|
|
46
|
+
error?: ExecutionError;
|
|
47
|
+
output?: Record<string, unknown>;
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
userId?: string;
|
|
50
|
+
createdAt: number;
|
|
51
|
+
updatedAt: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Query interface supporting both pipeline and scenario filtering
|
|
55
|
+
export interface BuildHistoryQuery {
|
|
56
|
+
pipelineId?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface BuildHistoryResponse {
|
|
60
|
+
entries: BuildHistoryEntry[];
|
|
61
|
+
total: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Storage interfaces - Simplified for pipeline-specific storage
|
|
65
|
+
export interface IBuildHistoryStorage {
|
|
66
|
+
save(entry: BuildHistoryEntry): Promise<void>;
|
|
67
|
+
get(id: string, pipelineId?: string): Promise<BuildHistoryEntry | undefined>;
|
|
68
|
+
getAll(): Promise<BuildHistoryEntry[]>;
|
|
69
|
+
getByPipeline(pipelineId: string): Promise<BuildHistoryEntry[]>;
|
|
70
|
+
update(id: string, updates: Partial<BuildHistoryEntry>, pipelineId?: string): Promise<void>;
|
|
71
|
+
delete(id: string, pipelineId?: string): Promise<void>;
|
|
72
|
+
clear(): Promise<void>;
|
|
73
|
+
getStorageInfo(): Promise<{
|
|
74
|
+
totalEntries: number;
|
|
75
|
+
totalSize: number;
|
|
76
|
+
oldestEntry?: number;
|
|
77
|
+
newestEntry?: number;
|
|
78
|
+
numberOfPipelines: number;
|
|
79
|
+
cachePath: string;
|
|
80
|
+
userDataPath: string;
|
|
81
|
+
retentionPolicy: {
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
maxEntries: number;
|
|
84
|
+
maxAge: number;
|
|
85
|
+
};
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Retention policy configuration
|
|
90
|
+
export interface RetentionPolicy {
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
maxEntries: number;
|
|
93
|
+
maxAge: number; // in milliseconds
|
|
94
|
+
maxSize: number; // in bytes
|
|
95
|
+
keepFailedBuilds: boolean;
|
|
96
|
+
keepSuccessfulBuilds: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Storage configuration
|
|
100
|
+
export interface BuildHistoryConfig {
|
|
101
|
+
storagePath: string;
|
|
102
|
+
indexFileName: string;
|
|
103
|
+
entryFilePrefix: string;
|
|
104
|
+
retentionPolicy: RetentionPolicy;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Authorization and subscription types
|
|
108
|
+
export interface SubscriptionBenefit {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
description?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface SubscriptionError extends Error {
|
|
115
|
+
code: "SUBSCRIPTION_REQUIRED" | "SUBSCRIPTION_EXPIRED" | "BENEFIT_NOT_FOUND" | "UNAUTHORIZED";
|
|
116
|
+
benefit?: string;
|
|
117
|
+
userMessage: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface AuthorizationContext {
|
|
121
|
+
userId?: string;
|
|
122
|
+
hasBenefit: (benefitId: string) => boolean;
|
|
123
|
+
isPaidUser: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Authorization check function type
|
|
127
|
+
export type AuthorizationCheck = (context: AuthorizationContext) => void;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMigration,
|
|
3
|
+
createMigrator,
|
|
4
|
+
finalVersion,
|
|
5
|
+
initialVersion,
|
|
6
|
+
OmitVersion,
|
|
7
|
+
SemVer,
|
|
8
|
+
} from "@pipelab/migration";
|
|
9
|
+
import {
|
|
10
|
+
AppConfig,
|
|
11
|
+
AppConfigV1,
|
|
12
|
+
AppConfigV2,
|
|
13
|
+
AppConfigV3,
|
|
14
|
+
AppConfigV4,
|
|
15
|
+
AppConfigV5,
|
|
16
|
+
AppConfigV6,
|
|
17
|
+
AppConfigV7,
|
|
18
|
+
} from "../config.schema";
|
|
19
|
+
import { FileRepoV1, FileRepoV2, FileRepo } from "./projects-types";
|
|
20
|
+
import { SavedFileV1, SavedFileV2, SavedFileV3, SavedFileV4, SavedFile } from "../model";
|
|
21
|
+
|
|
22
|
+
// --- Types ---
|
|
23
|
+
|
|
24
|
+
export interface Migrator<T> {
|
|
25
|
+
migrate: (data: any, options?: any) => Promise<T>;
|
|
26
|
+
defaultValue: T;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// --- Settings Migrator ---
|
|
30
|
+
|
|
31
|
+
const settingsMigratorInternal = createMigrator<AppConfigV1, AppConfig>();
|
|
32
|
+
|
|
33
|
+
export const defaultAppSettings = settingsMigratorInternal.createDefault({
|
|
34
|
+
cacheFolder: "",
|
|
35
|
+
clearTemporaryFoldersOnPipelineEnd: false,
|
|
36
|
+
locale: "en-US",
|
|
37
|
+
theme: "light",
|
|
38
|
+
version: "7.0.0" as SemVer,
|
|
39
|
+
autosave: true,
|
|
40
|
+
agents: [],
|
|
41
|
+
tours: {
|
|
42
|
+
dashboard: {
|
|
43
|
+
step: 0,
|
|
44
|
+
completed: false,
|
|
45
|
+
},
|
|
46
|
+
editor: {
|
|
47
|
+
step: 0,
|
|
48
|
+
completed: false,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const appSettingsMigrator = settingsMigratorInternal.createMigrations({
|
|
54
|
+
defaultValue: defaultAppSettings,
|
|
55
|
+
migrations: [
|
|
56
|
+
createMigration<never, AppConfigV1, AppConfigV2>({
|
|
57
|
+
version: "1.0.0" as SemVer,
|
|
58
|
+
up: (state) => state satisfies OmitVersion<AppConfigV2>,
|
|
59
|
+
down: initialVersion,
|
|
60
|
+
}),
|
|
61
|
+
createMigration<AppConfigV1, AppConfigV2, AppConfigV3>({
|
|
62
|
+
version: "2.0.0" as SemVer,
|
|
63
|
+
up: (state) => {
|
|
64
|
+
return {
|
|
65
|
+
...state,
|
|
66
|
+
clearTemporaryFoldersOnPipelineEnd: false,
|
|
67
|
+
} satisfies OmitVersion<AppConfigV3>;
|
|
68
|
+
},
|
|
69
|
+
down: () => {
|
|
70
|
+
throw new Error("Can't migrate down from 2.0.0");
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
createMigration<AppConfigV2, AppConfigV3, AppConfigV4>({
|
|
74
|
+
version: "3.0.0" as SemVer,
|
|
75
|
+
up: (state) => ({
|
|
76
|
+
...state,
|
|
77
|
+
locale: "en-US" as const,
|
|
78
|
+
}),
|
|
79
|
+
down: (state) => {
|
|
80
|
+
const { locale, ...rest } = state as AppConfigV4;
|
|
81
|
+
return rest as unknown as AppConfigV3;
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
createMigration<AppConfigV3, AppConfigV4, AppConfigV5>({
|
|
85
|
+
version: "4.0.0" as SemVer,
|
|
86
|
+
up: (state) => ({
|
|
87
|
+
...state,
|
|
88
|
+
tours: {
|
|
89
|
+
dashboard: {
|
|
90
|
+
step: 0,
|
|
91
|
+
completed: false,
|
|
92
|
+
},
|
|
93
|
+
editor: {
|
|
94
|
+
step: 0,
|
|
95
|
+
completed: false,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
down: (state) => {
|
|
100
|
+
const { tours, ...rest } = state as AppConfigV5;
|
|
101
|
+
return rest as unknown as AppConfigV4;
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
createMigration<AppConfigV4, AppConfigV5, AppConfigV6>({
|
|
105
|
+
version: "5.0.0" as SemVer,
|
|
106
|
+
up: (state) => ({
|
|
107
|
+
...state,
|
|
108
|
+
autosave: true,
|
|
109
|
+
}),
|
|
110
|
+
down: (state) => {
|
|
111
|
+
const { autosave, ...rest } = state as AppConfigV6;
|
|
112
|
+
return rest as unknown as AppConfigV5;
|
|
113
|
+
},
|
|
114
|
+
}),
|
|
115
|
+
createMigration<AppConfigV5, AppConfigV6, AppConfigV7>({
|
|
116
|
+
version: "6.0.0" as SemVer,
|
|
117
|
+
up: (state) => ({
|
|
118
|
+
...state,
|
|
119
|
+
agents: [],
|
|
120
|
+
}),
|
|
121
|
+
down: (state) => {
|
|
122
|
+
const { agents, ...rest } = state as AppConfigV7;
|
|
123
|
+
return rest as unknown as AppConfigV6;
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
createMigration<AppConfigV6, AppConfigV7, never>({
|
|
127
|
+
version: "7.0.0" as SemVer,
|
|
128
|
+
up: finalVersion,
|
|
129
|
+
down: () => {
|
|
130
|
+
throw new Error("Can't migrate down from 7.0.0");
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// --- Projects Migrator ---
|
|
137
|
+
|
|
138
|
+
const fileRepoMigratorInternal = createMigrator<FileRepoV1, FileRepo>();
|
|
139
|
+
|
|
140
|
+
export const defaultFileRepo = fileRepoMigratorInternal.createDefault({
|
|
141
|
+
version: "2.0.0",
|
|
142
|
+
projects: [
|
|
143
|
+
{
|
|
144
|
+
id: "main",
|
|
145
|
+
name: "Default project",
|
|
146
|
+
description: "The initial default project",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
pipelines: [],
|
|
150
|
+
proxies: [],
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
export const fileRepoMigrations = fileRepoMigratorInternal.createMigrations({
|
|
154
|
+
defaultValue: defaultFileRepo,
|
|
155
|
+
migrations: [
|
|
156
|
+
createMigration<never, FileRepoV1, FileRepoV2>({
|
|
157
|
+
version: "1.0.0",
|
|
158
|
+
up: (state) => {
|
|
159
|
+
const pipelines: FileRepoV2["pipelines"] = Object.entries(state.data || {}).map(
|
|
160
|
+
([id, file]) => {
|
|
161
|
+
return {
|
|
162
|
+
...file,
|
|
163
|
+
id,
|
|
164
|
+
project: "main",
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
return {
|
|
169
|
+
version: "2.0.0",
|
|
170
|
+
projects: [
|
|
171
|
+
{
|
|
172
|
+
id: "main",
|
|
173
|
+
name: "Default project",
|
|
174
|
+
description: "The initial default project",
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
pipelines: pipelines,
|
|
178
|
+
proxies: [],
|
|
179
|
+
} as any;
|
|
180
|
+
},
|
|
181
|
+
down: initialVersion,
|
|
182
|
+
}),
|
|
183
|
+
createMigration<FileRepoV1, FileRepoV2, never>({
|
|
184
|
+
version: "2.0.0",
|
|
185
|
+
up: finalVersion,
|
|
186
|
+
down: (state) => {
|
|
187
|
+
throw new Error("Cannot downgrade to version 1.0.0");
|
|
188
|
+
},
|
|
189
|
+
}),
|
|
190
|
+
],
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// --- Saved File Migrator ---
|
|
194
|
+
|
|
195
|
+
const savedFileMigratorInternal = createMigrator<SavedFileV1, SavedFile>();
|
|
196
|
+
const savedFileDefaultValue = savedFileMigratorInternal.createDefault({
|
|
197
|
+
canvas: {
|
|
198
|
+
triggers: [],
|
|
199
|
+
blocks: [],
|
|
200
|
+
},
|
|
201
|
+
description: "",
|
|
202
|
+
name: "",
|
|
203
|
+
variables: [],
|
|
204
|
+
type: "default",
|
|
205
|
+
version: "4.0.0" as SemVer,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
export const savedFileMigrator = savedFileMigratorInternal.createMigrations({
|
|
209
|
+
defaultValue: savedFileDefaultValue,
|
|
210
|
+
migrations: [
|
|
211
|
+
createMigration<never, SavedFileV1, SavedFileV2>({
|
|
212
|
+
version: "1.0.0" as SemVer,
|
|
213
|
+
up: (state) => {
|
|
214
|
+
const blocks = state.canvas.blocks;
|
|
215
|
+
|
|
216
|
+
const triggers: any[] = [];
|
|
217
|
+
const newBlocks: any[] = [];
|
|
218
|
+
|
|
219
|
+
for (const block of blocks) {
|
|
220
|
+
if (block.type === "event") {
|
|
221
|
+
triggers.push(block);
|
|
222
|
+
} else {
|
|
223
|
+
newBlocks.push(block);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
canvas: {
|
|
229
|
+
blocks: newBlocks,
|
|
230
|
+
triggers: triggers,
|
|
231
|
+
},
|
|
232
|
+
description: state.description,
|
|
233
|
+
name: state.name,
|
|
234
|
+
variables: state.variables,
|
|
235
|
+
} as any;
|
|
236
|
+
},
|
|
237
|
+
down: initialVersion,
|
|
238
|
+
}),
|
|
239
|
+
createMigration<SavedFileV1, SavedFileV2, SavedFileV3>({
|
|
240
|
+
version: "2.0.0" as SemVer,
|
|
241
|
+
up: (state) => {
|
|
242
|
+
const { canvas, ...rest } = state;
|
|
243
|
+
const { blocks, triggers } = canvas;
|
|
244
|
+
|
|
245
|
+
const newBlocks: any[] = [];
|
|
246
|
+
|
|
247
|
+
for (const block of blocks) {
|
|
248
|
+
const newParams: any = {};
|
|
249
|
+
|
|
250
|
+
for (const data of Object.entries(block.params)) {
|
|
251
|
+
if (data === undefined) {
|
|
252
|
+
throw new Error("Can't migrate block with undefined params");
|
|
253
|
+
} else {
|
|
254
|
+
const [key, value] = data;
|
|
255
|
+
newParams[key] = {
|
|
256
|
+
editor: "editor",
|
|
257
|
+
value,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
newBlocks.push({
|
|
263
|
+
...block,
|
|
264
|
+
params: newParams,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
...rest,
|
|
270
|
+
canvas: {
|
|
271
|
+
triggers,
|
|
272
|
+
blocks: newBlocks,
|
|
273
|
+
},
|
|
274
|
+
} as any;
|
|
275
|
+
},
|
|
276
|
+
down: () => {
|
|
277
|
+
throw new Error("Migration down not implemented");
|
|
278
|
+
},
|
|
279
|
+
}),
|
|
280
|
+
createMigration<SavedFileV2, SavedFileV3, SavedFileV4>({
|
|
281
|
+
version: "3.0.0" as SemVer,
|
|
282
|
+
up: (state) => {
|
|
283
|
+
return {
|
|
284
|
+
...state,
|
|
285
|
+
type: "default",
|
|
286
|
+
} as any;
|
|
287
|
+
},
|
|
288
|
+
down: () => {
|
|
289
|
+
throw new Error("Migration down not implemented");
|
|
290
|
+
},
|
|
291
|
+
}),
|
|
292
|
+
createMigration<SavedFileV3, SavedFileV4, SavedFileV4>({
|
|
293
|
+
version: "4.0.0" as SemVer,
|
|
294
|
+
up: finalVersion,
|
|
295
|
+
down: () => {
|
|
296
|
+
throw new Error("Migration down not implemented");
|
|
297
|
+
},
|
|
298
|
+
}),
|
|
299
|
+
],
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// --- Registry ---
|
|
303
|
+
|
|
304
|
+
export const configRegistry: Record<string, Migrator<any>> = {
|
|
305
|
+
settings: appSettingsMigrator,
|
|
306
|
+
projects: fileRepoMigrations,
|
|
307
|
+
pipeline: savedFileMigrator,
|
|
308
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SaveLocationValidator } from "../save-location";
|
|
2
|
+
import { object, string, optional, record, InferInput, literal, array } from "valibot";
|
|
3
|
+
|
|
4
|
+
export const FileRepoValidatorV1 = object({
|
|
5
|
+
version: literal("1.0.0"),
|
|
6
|
+
data: optional(record(string(), SaveLocationValidator), {}),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const FileRepoProjectValidatorV2 = object({
|
|
10
|
+
id: string(),
|
|
11
|
+
name: string(),
|
|
12
|
+
description: string(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const FileRepoValidatorV2 = object({
|
|
16
|
+
version: literal("2.0.0"),
|
|
17
|
+
projects: array(FileRepoProjectValidatorV2),
|
|
18
|
+
pipelines: optional(array(SaveLocationValidator), []),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type FileRepoV1 = InferInput<typeof FileRepoValidatorV1>;
|
|
22
|
+
export type FileRepoV2 = InferInput<typeof FileRepoValidatorV2>;
|
|
23
|
+
|
|
24
|
+
export const FileRepoValidator = FileRepoValidatorV2;
|
|
25
|
+
export type FileRepo = InferInput<typeof FileRepoValidator>;
|