@ridit/forge 0.2.6

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.
@@ -0,0 +1,590 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import type { Branch } from "../types/branch";
4
+ import type { Repo } from "../types/repo";
5
+ import type { Commit } from "../types/commit";
6
+ import { matchPaths } from "./forgeIgnore";
7
+ import { createCheckpoint } from "./checkpoint";
8
+ import type { FileBlob } from "../types/files";
9
+ import { determineFileStatus } from "./add";
10
+ import { commitInBranch, getCommit, getLatestCommitId } from "./commit";
11
+ import { updateRepo } from "./repo";
12
+ import type { Checkpoint } from "../types/checkpoint";
13
+ import { switchEmitter } from "./switchEvents";
14
+ import { checkoutCommit } from "./checkout";
15
+ import { readObject, writeObject } from "./objects";
16
+
17
+ function getBranchPaths(repo_path: string, branch_name?: string) {
18
+ const forgeFolder = path.join(repo_path, ".forge");
19
+ const branchesFolder = path.join(forgeFolder, "branches");
20
+ const branchFolder = branch_name
21
+ ? path.join(branchesFolder, branch_name)
22
+ : undefined;
23
+ const branchDataFile = branchFolder
24
+ ? path.join(branchFolder, "branch.json")
25
+ : undefined;
26
+ return { forgeFolder, branchesFolder, branchFolder, branchDataFile };
27
+ }
28
+
29
+ export function createBranch(
30
+ repo_path: string,
31
+ branch_name: string,
32
+ ): { status: "ok" | "error"; error?: string } {
33
+ const { branchDataFile, branchFolder, branchesFolder } = getBranchPaths(
34
+ repo_path,
35
+ branch_name,
36
+ );
37
+ const branchCommitsFolder = path.join(branchFolder!, "commits");
38
+
39
+ if (!fs.existsSync(branchesFolder))
40
+ fs.mkdirSync(branchesFolder, { recursive: true });
41
+
42
+ try {
43
+ fs.mkdirSync(branchFolder!, { recursive: true });
44
+ fs.mkdirSync(branchCommitsFolder, { recursive: true });
45
+
46
+ fs.writeFileSync(
47
+ branchDataFile!,
48
+ JSON.stringify({ name: branch_name, latestCommitId: "" } as Branch),
49
+ );
50
+
51
+ const currentBranch = getCurrentBranch(repo_path);
52
+ if (currentBranch.error || !currentBranch.branch) {
53
+ commitInBranch("Initial commit", repo_path, branch_name);
54
+ } else {
55
+ const branch = currentBranch.branch;
56
+ const commitsFolder = path.join(
57
+ repo_path,
58
+ ".forge",
59
+ "branches",
60
+ branch.name,
61
+ "commits",
62
+ );
63
+ const newBranchCommitsFolder = path.join(
64
+ repo_path,
65
+ ".forge",
66
+ "branches",
67
+ branch_name,
68
+ "commits",
69
+ );
70
+
71
+ fs.cpSync(commitsFolder, newBranchCommitsFolder, { recursive: true });
72
+
73
+ fs.writeFileSync(
74
+ branchDataFile!,
75
+ JSON.stringify({
76
+ name: branch_name,
77
+ latestCommitId: branch.latestCommitId,
78
+ } as Branch),
79
+ );
80
+ }
81
+ } catch (err) {
82
+ return { status: "error", error: err as string };
83
+ }
84
+
85
+ return { status: "ok" };
86
+ }
87
+
88
+ export function getBranch(
89
+ repo_path: string,
90
+ branch_name: string,
91
+ ): { status: "ok" | "error"; branch?: Branch; error?: string } {
92
+ const { branchDataFile, branchFolder, branchesFolder } = getBranchPaths(
93
+ repo_path,
94
+ branch_name,
95
+ );
96
+
97
+ if (!fs.existsSync(branchesFolder))
98
+ return {
99
+ status: "error",
100
+ error: "branches folder is missing, consider reinitialize repo.",
101
+ };
102
+
103
+ if (!fs.existsSync(branchFolder!))
104
+ return {
105
+ status: "error",
106
+ error: `${branch_name} doesn't exists.`,
107
+ };
108
+
109
+ if (!fs.existsSync(branchDataFile!))
110
+ return {
111
+ status: "error",
112
+ error: `${branch_name} meta data is missing, consider reinitialize the branch.`,
113
+ };
114
+
115
+ const branchData = JSON.parse(fs.readFileSync(branchDataFile!, "utf-8"));
116
+
117
+ return { status: "ok", branch: branchData };
118
+ }
119
+
120
+ export function updateBranch(
121
+ repo_path: string,
122
+ branch_name: string,
123
+ latest_commit_id: string,
124
+ ): { status: "ok" | "error"; error?: string } {
125
+ const { branchDataFile, branchFolder, branchesFolder } = getBranchPaths(
126
+ repo_path,
127
+ branch_name,
128
+ );
129
+
130
+ if (!fs.existsSync(branchesFolder))
131
+ return {
132
+ status: "error",
133
+ error: "branches folder is missing, consider reinitialize repo.",
134
+ };
135
+
136
+ if (!fs.existsSync(branchFolder!))
137
+ return {
138
+ status: "error",
139
+ error: `${branch_name} doesn't exists.`,
140
+ };
141
+
142
+ if (!fs.existsSync(branchDataFile!))
143
+ return {
144
+ status: "error",
145
+ error: `${branch_name} meta data is missing, consider reinitialize the branch.`,
146
+ };
147
+
148
+ const branchData = JSON.parse(
149
+ fs.readFileSync(branchDataFile!, "utf-8"),
150
+ ) as Branch;
151
+ const newData = { ...branchData, latestCommitId: latest_commit_id } as Branch;
152
+
153
+ fs.writeFileSync(branchDataFile!, JSON.stringify(newData));
154
+
155
+ return { status: "ok" };
156
+ }
157
+
158
+ export function getCurrentBranch(repo_path: string): {
159
+ status: "ok" | "error";
160
+ branch?: Branch;
161
+ error?: string;
162
+ } {
163
+ const { branchesFolder } = getBranchPaths(repo_path);
164
+ const forgeFolder = path.join(repo_path, ".forge");
165
+ const repoDataFile = path.join(forgeFolder, "repo.json");
166
+
167
+ if (!fs.existsSync(branchesFolder))
168
+ return {
169
+ status: "error",
170
+ error: "branches folder is missing, consider reinitialize repo.",
171
+ };
172
+
173
+ if (!fs.existsSync(repoDataFile))
174
+ return {
175
+ status: "error",
176
+ error: `repo meta data is missing, consider reinitialize the branch.`,
177
+ };
178
+
179
+ const branchName = (
180
+ JSON.parse(fs.readFileSync(repoDataFile, "utf-8")) as Repo
181
+ ).branch;
182
+
183
+ const branchFolder = path.join(branchesFolder, branchName);
184
+ const branchDataFile = path.join(branchFolder, "branch.json");
185
+
186
+ if (!fs.existsSync(branchFolder))
187
+ return {
188
+ status: "error",
189
+ error: `${branchName} doesn't exists, consider reinitializing the repo.`,
190
+ };
191
+
192
+ if (!fs.existsSync(branchDataFile))
193
+ return {
194
+ status: "error",
195
+ error: `${branchName} meta data is missing, consider reinitialize the branch.`,
196
+ };
197
+
198
+ const branchData = JSON.parse(
199
+ fs.readFileSync(branchDataFile, "utf-8"),
200
+ ) as Branch;
201
+
202
+ return { status: "ok", branch: branchData };
203
+ }
204
+
205
+ export function listBranches(repo_path: string): {
206
+ status: "ok" | "error";
207
+ branches?: Branch[];
208
+ error?: string;
209
+ } {
210
+ const { branchesFolder } = getBranchPaths(repo_path);
211
+
212
+ if (!fs.existsSync(branchesFolder))
213
+ return {
214
+ status: "error",
215
+ error: "branches folder is missing, consider reinitialize repo.",
216
+ };
217
+
218
+ const files = fs.readdirSync(branchesFolder);
219
+ const branches = files.map((file) => {
220
+ const branch = JSON.parse(
221
+ fs.readFileSync(
222
+ path.join(branchesFolder, file.toString(), "branch.json"),
223
+ "utf-8",
224
+ ),
225
+ ) as Branch;
226
+ return branch;
227
+ });
228
+
229
+ return { status: "ok", branches };
230
+ }
231
+
232
+ function processFiles(basePath: string, files: (string | Buffer)[]) {
233
+ const normalized = files
234
+ .map((f) => path.join(basePath, f.toString()).replace(/\\/g, "/"))
235
+ .filter((p) => {
236
+ try {
237
+ return fs.statSync(p).isFile();
238
+ } catch {
239
+ return false;
240
+ }
241
+ });
242
+
243
+ const checkedFiles = matchPaths(normalized);
244
+
245
+ return checkedFiles;
246
+ }
247
+
248
+ export function switchBranch(
249
+ repo_path: string,
250
+ branch_name: string,
251
+ ): { status: "ok" | "error"; error?: string } {
252
+ const currentBranch = getCurrentBranch(repo_path);
253
+ if (currentBranch.error || !currentBranch.branch)
254
+ return { status: "error", error: "no current branch found" };
255
+
256
+ const list = fs.readdirSync(".", { recursive: true });
257
+ const files = processFiles(".", list);
258
+
259
+ const latestCommitId = getLatestCommitId(
260
+ repo_path,
261
+ currentBranch.branch.name,
262
+ );
263
+ if (latestCommitId.error)
264
+ return { status: "error", error: latestCommitId.error };
265
+
266
+ const fileBlobs = files.files?.map((f) => ({
267
+ name: path.basename(f),
268
+ path: f,
269
+ hash: writeObject(repo_path, fs.readFileSync(f).toString()),
270
+ status: determineFileStatus(repo_path, f).file_status ?? "untracked",
271
+ })) as FileBlob[];
272
+
273
+ const res = createCheckpoint(
274
+ repo_path,
275
+ fileBlobs,
276
+ currentBranch.branch.name,
277
+ latestCommitId.latestCommitId ?? "",
278
+ );
279
+ if (res.error) return { status: "error", error: res.error };
280
+ switchEmitter.emit("event", {
281
+ type: "checkpoint_created",
282
+ branch: currentBranch.branch.name,
283
+ });
284
+
285
+ files.files?.forEach((f) => fs.rmSync(f, { recursive: true }));
286
+ switchEmitter.emit("event", {
287
+ type: "files_deleted",
288
+ files: files.files ?? [],
289
+ });
290
+
291
+ const newBranch = getBranch(repo_path, branch_name);
292
+ if (newBranch.error || !newBranch.branch)
293
+ return {
294
+ status: "error",
295
+ error: newBranch.error || `${branch_name} not found`,
296
+ };
297
+
298
+ const latestCommit = newBranch.branch.latestCommitId
299
+ ? getCommit(repo_path, newBranch.branch.latestCommitId, branch_name)
300
+ : null;
301
+ if (latestCommit?.error)
302
+ return { status: "error", error: latestCommit.error };
303
+
304
+ const checkpointFile = path.join(
305
+ repo_path,
306
+ ".forge",
307
+ "branches",
308
+ branch_name,
309
+ "checkpoint.json",
310
+ );
311
+ const hasCheckpoint = fs.existsSync(checkpointFile);
312
+
313
+ if (hasCheckpoint) {
314
+ const checkpointData = JSON.parse(
315
+ fs.readFileSync(checkpointFile, "utf-8"),
316
+ ) as Checkpoint;
317
+ const targetLatestCommitId = getLatestCommitId(repo_path, branch_name);
318
+
319
+ const checkpointIsLatest =
320
+ targetLatestCommitId.latestCommitId === checkpointData.commitId ||
321
+ new Date(checkpointData.date) >=
322
+ new Date(latestCommit?.commit?.date ?? 0);
323
+
324
+ if (checkpointIsLatest) {
325
+ checkpointData.files.forEach((file) =>
326
+ fs.writeFileSync(file.path, readObject(repo_path, file.hash)),
327
+ );
328
+ switchEmitter.emit("event", {
329
+ type: "files_restored",
330
+ files: checkpointData.files.map((f) => f.path),
331
+ source: "checkpoint",
332
+ });
333
+ } else {
334
+ latestCommit?.commit?.fileBlobs.forEach((file) =>
335
+ fs.writeFileSync(file.path, readObject(repo_path, file.hash)),
336
+ );
337
+
338
+ switchEmitter.emit("event", {
339
+ type: "files_restored",
340
+ files: latestCommit?.commit?.fileBlobs.map((f) => f.path) ?? [],
341
+ source: "commit",
342
+ });
343
+ }
344
+ } else {
345
+ latestCommit?.commit?.fileBlobs.forEach((file) =>
346
+ fs.writeFileSync(file.path, readObject(repo_path, file.hash)),
347
+ );
348
+ switchEmitter.emit("event", {
349
+ type: "files_restored",
350
+ files: latestCommit?.commit?.fileBlobs.map((f) => f.path) ?? [],
351
+ source: "commit",
352
+ });
353
+ }
354
+
355
+ const res_2 = updateRepo(repo_path, { branch: branch_name });
356
+ if (res_2.error) return { status: "error", error: res_2.error };
357
+
358
+ switchEmitter.emit("event", {
359
+ type: "switched",
360
+ from: currentBranch.branch.name,
361
+ to: branch_name,
362
+ });
363
+
364
+ return { status: "ok" };
365
+ }
366
+
367
+ export function getCommitHistory(
368
+ repo_path: string,
369
+ branch_name: string,
370
+ ): string[] {
371
+ const commitIds: string[] = [];
372
+ const latestCommitId = getLatestCommitId(repo_path, branch_name);
373
+ if (!latestCommitId.latestCommitId) return commitIds;
374
+
375
+ let currentId: string | undefined = latestCommitId.latestCommitId;
376
+
377
+ while (currentId) {
378
+ commitIds.push(currentId);
379
+ const commit = getCommit(repo_path, currentId, branch_name);
380
+ currentId = commit.commit?.parent;
381
+ }
382
+
383
+ return commitIds;
384
+ }
385
+
386
+ export function mergeBranch(
387
+ current_branch: string,
388
+ target_branch: string,
389
+ repo_path: string,
390
+ ): { status: "ok" | "error"; error?: string } {
391
+ const currentBranchCommitHistory = getCommitHistory(
392
+ repo_path,
393
+ current_branch,
394
+ );
395
+ const targetBranchommitHistory = getCommitHistory(repo_path, target_branch);
396
+
397
+ let sharedCommit;
398
+
399
+ for (const cb of currentBranchCommitHistory) {
400
+ if (targetBranchommitHistory.includes(cb)) {
401
+ sharedCommit = cb;
402
+ break;
403
+ }
404
+ }
405
+
406
+ if (!sharedCommit)
407
+ return { status: "error", error: "no shared commit found." };
408
+
409
+ const latestCommitIdOfCurrentBranch = getLatestCommitId(
410
+ repo_path,
411
+ current_branch,
412
+ );
413
+ if (
414
+ latestCommitIdOfCurrentBranch.error ||
415
+ !latestCommitIdOfCurrentBranch.latestCommitId
416
+ )
417
+ return {
418
+ status: "error",
419
+ error: `no current latest commit in ${current_branch}`,
420
+ };
421
+
422
+ let conflict: boolean = false;
423
+ let targetChanged: FileBlob[] = [];
424
+
425
+ if (latestCommitIdOfCurrentBranch.latestCommitId === sharedCommit) {
426
+ conflict = false;
427
+ } else {
428
+ const latestCommitIdOfTargetBranch = getLatestCommitId(
429
+ repo_path,
430
+ target_branch,
431
+ );
432
+ if (
433
+ latestCommitIdOfTargetBranch.error ||
434
+ !latestCommitIdOfTargetBranch.latestCommitId
435
+ )
436
+ return {
437
+ status: "error",
438
+ error: `no current latest commit in ${target_branch}`,
439
+ };
440
+
441
+ const sharedCommitOfCurrentBranch = getCommit(
442
+ repo_path,
443
+ sharedCommit,
444
+ current_branch,
445
+ );
446
+ const latestCommitOfTargetBranch = getCommit(
447
+ repo_path,
448
+ latestCommitIdOfTargetBranch.latestCommitId,
449
+ target_branch,
450
+ );
451
+ const latestCommitOfCurrentBranch = getCommit(
452
+ repo_path,
453
+ latestCommitIdOfCurrentBranch.latestCommitId,
454
+ current_branch,
455
+ );
456
+
457
+ if (
458
+ sharedCommitOfCurrentBranch.error ||
459
+ !sharedCommitOfCurrentBranch.commit ||
460
+ latestCommitOfTargetBranch.error ||
461
+ !latestCommitOfTargetBranch.commit ||
462
+ latestCommitOfCurrentBranch.error ||
463
+ !latestCommitOfCurrentBranch.commit
464
+ )
465
+ return { status: "error", error: `` };
466
+
467
+ const currentChanged = latestCommitOfCurrentBranch.commit.fileBlobs.filter(
468
+ (f) =>
469
+ sharedCommitOfCurrentBranch.commit!.fileBlobs.find(
470
+ (s) => s.path === f.path,
471
+ )?.hash !== f.hash,
472
+ );
473
+
474
+ targetChanged = latestCommitOfTargetBranch.commit.fileBlobs.filter(
475
+ (f) =>
476
+ sharedCommitOfCurrentBranch.commit!.fileBlobs.find(
477
+ (s) => s.path === f.path,
478
+ )?.hash !== f.hash,
479
+ );
480
+
481
+ conflict = currentChanged.some((cf) =>
482
+ targetChanged.find((tf) => tf.path === cf.path),
483
+ );
484
+
485
+ if (conflict) {
486
+ const conflictFiles = currentChanged
487
+ .filter((cf) => targetChanged.find((tf) => tf.path === cf.path))
488
+ .map((f) => f.path);
489
+ switchEmitter.emit("event", {
490
+ type: "merge_conflict_detected",
491
+ files: conflictFiles,
492
+ });
493
+ return { status: "error", error: "conflict found." };
494
+ }
495
+ }
496
+
497
+ const targetBranchCommitFolder = path.join(
498
+ repo_path,
499
+ ".forge",
500
+ "branches",
501
+ target_branch,
502
+ "commits",
503
+ );
504
+ const currentBranchCommitFolder = path.join(
505
+ repo_path,
506
+ ".forge",
507
+ "branches",
508
+ current_branch,
509
+ "commits",
510
+ );
511
+
512
+ fs.cpSync(targetBranchCommitFolder, currentBranchCommitFolder, {
513
+ recursive: true,
514
+ force: true,
515
+ });
516
+
517
+ const latestCommitIdOfTargetBranch = getLatestCommitId(
518
+ repo_path,
519
+ target_branch,
520
+ );
521
+ if (
522
+ latestCommitIdOfTargetBranch.error ||
523
+ !latestCommitIdOfTargetBranch.latestCommitId
524
+ )
525
+ return {
526
+ status: "error",
527
+ error: `no latest commit found in ${target_branch}`,
528
+ };
529
+
530
+ updateBranch(
531
+ repo_path,
532
+ current_branch,
533
+ latestCommitIdOfTargetBranch.latestCommitId,
534
+ );
535
+
536
+ const latestCommitIdOfCurrentBranch2 = getLatestCommitId(
537
+ repo_path,
538
+ current_branch,
539
+ );
540
+ if (
541
+ latestCommitIdOfCurrentBranch2.error ||
542
+ !latestCommitIdOfCurrentBranch2.latestCommitId
543
+ )
544
+ return {
545
+ status: "error",
546
+ error: `no latest commit found in ${current_branch}`,
547
+ };
548
+
549
+ checkoutCommit(
550
+ latestCommitIdOfCurrentBranch2.latestCommitId,
551
+ repo_path,
552
+ current_branch,
553
+ );
554
+
555
+ switchEmitter.emit("event", {
556
+ type: "merge_complete",
557
+ from: target_branch,
558
+ to: current_branch,
559
+ filesChanged: targetChanged.length,
560
+ });
561
+
562
+ return { status: "ok" };
563
+ }
564
+
565
+ export function deleteBranch(
566
+ repo_path: string,
567
+ branch_name: string,
568
+ ): { status: "ok" | "error"; error?: string } {
569
+ const { branchFolder, branchesFolder } = getBranchPaths(
570
+ repo_path,
571
+ branch_name,
572
+ );
573
+
574
+ if (!fs.existsSync(branchesFolder))
575
+ return {
576
+ status: "error",
577
+ error: "branches folder is missing, consider reinitialize repo.",
578
+ };
579
+
580
+ if (!fs.existsSync(branchFolder!))
581
+ return { status: "error", error: `${branch_name} doesn't exists.` };
582
+
583
+ const currentBranch = getCurrentBranch(repo_path);
584
+ if (currentBranch.branch?.name === branch_name)
585
+ return { status: "error", error: `cannot delete current branch.` };
586
+
587
+ fs.rmSync(branchFolder!, { recursive: true, force: true });
588
+
589
+ return { status: "ok" };
590
+ }
@@ -0,0 +1,57 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import type { Commit } from "../types/commit";
4
+ import { getCurrentBranch } from "./branch";
5
+ import { readObject } from "./objects";
6
+
7
+ export function checkoutCommit(
8
+ commitId: string,
9
+ repo_path: string,
10
+ branch_name?: string,
11
+ ): {
12
+ status: "ok" | "error";
13
+ error?: string;
14
+ } {
15
+ const forgeFolder = path.join(repo_path, ".forge");
16
+ const currentBranch = getCurrentBranch(repo_path);
17
+ if (currentBranch.error || !currentBranch.branch) {
18
+ return {
19
+ status: "error",
20
+ error: currentBranch.error || "no current branch found.",
21
+ };
22
+ }
23
+ const branchName = branch_name ?? currentBranch.branch.name;
24
+ const commitFolder = path.join(
25
+ forgeFolder,
26
+ "branches",
27
+ branchName,
28
+ "commits",
29
+ );
30
+ const commitFile = path.join(commitFolder, `${commitId}.json`);
31
+
32
+ if (!fs.existsSync(commitFolder))
33
+ return {
34
+ status: "error",
35
+ error: "commits folder is missing, consider reinitialize repo.",
36
+ };
37
+
38
+ if (!fs.existsSync(commitFile))
39
+ return {
40
+ status: "error",
41
+ error: "commit not found.",
42
+ };
43
+
44
+ const commitData: Commit = JSON.parse(fs.readFileSync(commitFile, "utf-8"));
45
+ const files = commitData.fileBlobs;
46
+
47
+ console.log(
48
+ "restoring files:",
49
+ files.map((f) => f.path),
50
+ );
51
+
52
+ files.forEach((file) => {
53
+ fs.writeFileSync(file.path, readObject(repo_path, file.hash));
54
+ });
55
+
56
+ return { status: "ok" };
57
+ }
@@ -0,0 +1,52 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import type { Checkpoint } from "../types/checkpoint";
4
+ import type { FileBlob } from "../types/files";
5
+
6
+ export function createCheckpoint(
7
+ repo_path: string,
8
+ fileBlobs: FileBlob[],
9
+ branch_name: string,
10
+ commitId: string,
11
+ ): { status: "ok" | "error"; error?: string } {
12
+ const forgeFolder = path.join(repo_path, ".forge");
13
+ const checkpointsFolder = path.join(forgeFolder, "branches", branch_name);
14
+ [];
15
+
16
+ if (!fs.existsSync(checkpointsFolder))
17
+ fs.mkdirSync(checkpointsFolder, { recursive: true });
18
+
19
+ const checkpointData = {
20
+ date: new Date().toISOString(),
21
+ files: fileBlobs,
22
+ } as Checkpoint;
23
+
24
+ const checkpointFile = path.join(checkpointsFolder, "checkpoint.json");
25
+ fs.writeFileSync(checkpointFile, JSON.stringify(checkpointData));
26
+
27
+ return { status: "ok" };
28
+ }
29
+
30
+ export function getCheckpoint(
31
+ repo_path: string,
32
+ branch_name: string,
33
+ ): { status: "ok" | "error"; checkpoint?: Checkpoint; error?: string } {
34
+ const forgeFolder = path.join(repo_path, ".forge");
35
+ const checkpointsFolder = path.join(forgeFolder, "branches", branch_name);
36
+
37
+ if (!fs.existsSync(checkpointsFolder))
38
+ return {
39
+ status: "error",
40
+ error: "checkpoints folder is missing, consider reinitializing repo.",
41
+ };
42
+
43
+ const checkpointsFile = path.join(checkpointsFolder, "checkpoint.json");
44
+ if (!fs.existsSync(checkpointsFile))
45
+ return { status: "error", error: `checkpoint doesn't exists.` };
46
+
47
+ const checkpointData = JSON.parse(
48
+ fs.readFileSync(checkpointsFile, "utf-8"),
49
+ ) as Checkpoint;
50
+
51
+ return { status: "ok", checkpoint: checkpointData };
52
+ }