@scriptdb/storage 1.0.9 → 1.1.1

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/dist/index.mjs ADDED
@@ -0,0 +1,655 @@
1
+ // src/index.ts
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { simpleGit } from "simple-git";
5
+ var Storage = class {
6
+ constructor(repoPath, gitRoot) {
7
+ this.repo = null;
8
+ this.repoPath = repoPath;
9
+ this.gitRoot = gitRoot || repoPath;
10
+ }
11
+ async initialize(repoPath = null) {
12
+ try {
13
+ if (repoPath) {
14
+ this.repoPath = repoPath;
15
+ }
16
+ if (!fs.existsSync(this.repoPath)) {
17
+ fs.mkdirSync(this.repoPath, { recursive: true });
18
+ }
19
+ this.repo = simpleGit(this.gitRoot);
20
+ if (!fs.existsSync(path.join(this.gitRoot, ".git"))) {
21
+ try {
22
+ await this.repo.init();
23
+ await this.setupBasicConfig();
24
+ } catch (error) {
25
+ throw new Error(`Failed to initialize git repository: ${error.message}`);
26
+ }
27
+ }
28
+ return this.repo;
29
+ } catch (error) {
30
+ throw new Error(`Failed to initialize repository: ${error.message}`);
31
+ }
32
+ }
33
+ async addFile(filePath, content) {
34
+ try {
35
+ const fullPath = path.join(this.repoPath, filePath);
36
+ const dir = path.dirname(fullPath);
37
+ if (!fs.existsSync(dir)) {
38
+ fs.mkdirSync(dir, { recursive: true });
39
+ }
40
+ fs.writeFileSync(fullPath, content);
41
+ if (!this.repo) {
42
+ throw new Error("Repository not initialized. Call initialize() first.");
43
+ }
44
+ const gitRelativePath = path.relative(this.gitRoot, fullPath);
45
+ await this.repo.add(gitRelativePath);
46
+ return {
47
+ success: true,
48
+ message: `File ${filePath} added successfully`,
49
+ path: fullPath
50
+ };
51
+ } catch (error) {
52
+ return {
53
+ success: false,
54
+ message: `Failed to add file: ${error.message}`
55
+ };
56
+ }
57
+ }
58
+ async updateFile(filePath, content) {
59
+ try {
60
+ const fullPath = path.join(this.repoPath, filePath);
61
+ if (!fs.existsSync(fullPath)) {
62
+ return {
63
+ success: false,
64
+ message: `File ${filePath} does not exist`
65
+ };
66
+ }
67
+ fs.writeFileSync(fullPath, content);
68
+ if (!this.repo) {
69
+ throw new Error("Repository not initialized. Call initialize() first.");
70
+ }
71
+ const gitRelativePath = path.relative(this.gitRoot, fullPath);
72
+ await this.repo.add(gitRelativePath);
73
+ return {
74
+ success: true,
75
+ message: `File ${filePath} updated successfully`,
76
+ path: fullPath
77
+ };
78
+ } catch (error) {
79
+ return {
80
+ success: false,
81
+ message: `Failed to update file: ${error.message}`
82
+ };
83
+ }
84
+ }
85
+ async getFile(filePath) {
86
+ try {
87
+ const fullPath = path.join(this.repoPath, filePath);
88
+ if (!fs.existsSync(fullPath)) {
89
+ return {
90
+ success: false,
91
+ message: `File ${filePath} does not exist`
92
+ };
93
+ }
94
+ const content = fs.readFileSync(fullPath, "utf8");
95
+ return {
96
+ success: true,
97
+ content,
98
+ path: fullPath
99
+ };
100
+ } catch (error) {
101
+ return {
102
+ success: false,
103
+ message: `Failed to read file: ${error.message}`
104
+ };
105
+ }
106
+ }
107
+ async deleteFile(filePath) {
108
+ try {
109
+ const fullPath = path.join(this.repoPath, filePath);
110
+ if (!fs.existsSync(fullPath)) {
111
+ return {
112
+ success: false,
113
+ message: `File ${filePath} does not exist`
114
+ };
115
+ }
116
+ fs.unlinkSync(fullPath);
117
+ if (!this.repo) {
118
+ throw new Error("Repository not initialized. Call initialize() first.");
119
+ }
120
+ const gitRelativePath = path.relative(this.gitRoot, fullPath);
121
+ await this.repo.add(gitRelativePath);
122
+ return {
123
+ success: true,
124
+ message: `File ${filePath} deleted successfully`
125
+ };
126
+ } catch (error) {
127
+ return {
128
+ success: false,
129
+ message: `Failed to delete file: ${error.message}`
130
+ };
131
+ }
132
+ }
133
+ async commit(message) {
134
+ try {
135
+ const status = await this.getStatus();
136
+ if (status.staged.length === 0) {
137
+ return {
138
+ success: false,
139
+ message: "No staged changes to commit"
140
+ };
141
+ }
142
+ if (!this.repo) {
143
+ throw new Error("Repository not initialized. Call initialize() first.");
144
+ }
145
+ await this.repo.commit(message);
146
+ return {
147
+ success: true,
148
+ message: "Changes committed successfully"
149
+ };
150
+ } catch (error) {
151
+ return {
152
+ success: false,
153
+ message: `Failed to commit changes: ${error.message}`
154
+ };
155
+ }
156
+ }
157
+ async getStatus() {
158
+ try {
159
+ if (!this.repo) {
160
+ throw new Error("Repository not initialized. Call initialize() first.");
161
+ }
162
+ const status = await this.repo.status();
163
+ const staged = [];
164
+ const unstaged = [];
165
+ const untracked = [];
166
+ status.modified.forEach((file) => {
167
+ unstaged.push(file);
168
+ });
169
+ status.created.forEach((file) => {
170
+ staged.push(file);
171
+ });
172
+ status.deleted.forEach((file) => {
173
+ unstaged.push(file);
174
+ });
175
+ status.renamed.forEach((file) => {
176
+ staged.push(file.to);
177
+ });
178
+ status.not_added.forEach((file) => {
179
+ untracked.push(file);
180
+ });
181
+ status.staged.forEach((file) => {
182
+ staged.push(file);
183
+ });
184
+ return {
185
+ staged,
186
+ unstaged,
187
+ untracked,
188
+ clean: status.isClean()
189
+ };
190
+ } catch (error) {
191
+ return {
192
+ staged: [],
193
+ unstaged: [],
194
+ untracked: [],
195
+ clean: false,
196
+ error: error.message
197
+ };
198
+ }
199
+ }
200
+ async getHistory(filePath) {
201
+ try {
202
+ if (!this.repo) {
203
+ throw new Error("Repository not initialized. Call initialize() first.");
204
+ }
205
+ const log = await this.repo.log({ file: filePath });
206
+ return log.all.map((commit) => ({
207
+ hash: commit.hash.substring(0, 7),
208
+ message: commit.message || "",
209
+ date: commit.date || "",
210
+ author: commit.author_name || ""
211
+ }));
212
+ } catch (error) {
213
+ return [];
214
+ }
215
+ }
216
+ /**
217
+ * Set Git configuration values
218
+ * @param config - Configuration object
219
+ * @param scope - Configuration scope ('local', 'global', 'system')
220
+ * @returns Result of the operation
221
+ */
222
+ async setConfig(config, scope = "local") {
223
+ try {
224
+ if (!this.repo) {
225
+ throw new Error("Repository not initialized. Call initialize() first.");
226
+ }
227
+ const results = [];
228
+ if (config.userName) {
229
+ await this.repo.raw(["config", `--${scope}`, "user.name", config.userName]);
230
+ results.push({ key: "user.name", value: config.userName, scope });
231
+ }
232
+ if (config.userEmail) {
233
+ await this.repo.raw(["config", `--${scope}`, "user.email", config.userEmail]);
234
+ results.push({ key: "user.email", value: config.userEmail, scope });
235
+ }
236
+ if (config.options && typeof config.options === "object") {
237
+ for (const [key, value] of Object.entries(config.options)) {
238
+ await this.repo.raw(["config", `--${scope}`, key, value]);
239
+ results.push({ key, value, scope });
240
+ }
241
+ }
242
+ return {
243
+ success: true,
244
+ message: `Configuration updated successfully`,
245
+ changes: results
246
+ };
247
+ } catch (error) {
248
+ return {
249
+ success: false,
250
+ message: `Failed to set configuration: ${error.message}`
251
+ };
252
+ }
253
+ }
254
+ /**
255
+ * Get Git configuration value
256
+ * @param key - Configuration key (e.g., 'user.name')
257
+ * @param scope - Configuration scope ('local', 'global', 'system')
258
+ * @returns Result containing the configuration value
259
+ */
260
+ async getConfig(key, scope = "local") {
261
+ try {
262
+ if (!this.repo) {
263
+ throw new Error("Repository not initialized. Call initialize() first.");
264
+ }
265
+ const result = await this.repo.raw(["config", `--${scope}`, "--get", key]);
266
+ return {
267
+ success: true,
268
+ key,
269
+ value: result.trim(),
270
+ scope
271
+ };
272
+ } catch (error) {
273
+ if (error.message.includes("exit code: 1")) {
274
+ return {
275
+ success: false,
276
+ message: `Configuration key '${key}' not found in ${scope} scope`
277
+ };
278
+ }
279
+ return {
280
+ success: false,
281
+ message: `Failed to get configuration for ${key}: ${error.message}`
282
+ };
283
+ }
284
+ }
285
+ /**
286
+ * Get all Git configuration values
287
+ * @param scope - Configuration scope ('local', 'global', 'system')
288
+ * @returns Result containing all configuration values
289
+ */
290
+ async listConfig(scope = "local") {
291
+ try {
292
+ if (!this.repo) {
293
+ throw new Error("Repository not initialized. Call initialize() first.");
294
+ }
295
+ const result = await this.repo.raw(["config", `--${scope}`, "--list"]);
296
+ const lines = result.trim().split("\n");
297
+ const config = {};
298
+ lines.forEach((line) => {
299
+ const [key, value] = line.split("=");
300
+ if (key) {
301
+ config[key] = value || "";
302
+ }
303
+ });
304
+ return {
305
+ success: true,
306
+ scope,
307
+ config
308
+ };
309
+ } catch (error) {
310
+ return {
311
+ success: false,
312
+ message: `Failed to list configuration: ${error.message}`,
313
+ scope
314
+ };
315
+ }
316
+ }
317
+ /**
318
+ * Set up a basic configuration for the repository
319
+ * @param userName - User name (optional, defaults to 'Sandbox Storage')
320
+ * @param userEmail - User email (optional, defaults to 'sandbox@example.com')
321
+ * @param additionalConfig - Additional configuration options (optional)
322
+ * @returns Result of the operation
323
+ */
324
+ async setupBasicConfig(userName = "Sandbox Storage", userEmail = "sandbox@example.com", additionalConfig = {}) {
325
+ const config = {
326
+ userName,
327
+ userEmail,
328
+ options: {
329
+ "core.autocrlf": "false",
330
+ // Important for cross-platform compatibility
331
+ "core.filemode": "false",
332
+ // Important for cross-platform compatibility
333
+ ...additionalConfig
334
+ }
335
+ };
336
+ return this.setConfig(config, "local");
337
+ }
338
+ /**
339
+ * Add a remote repository
340
+ * @param name - Remote name (e.g., 'origin')
341
+ * @param url - Remote repository URL
342
+ * @returns Result of the operation
343
+ */
344
+ async addRemote(name, url) {
345
+ try {
346
+ if (!this.repo) {
347
+ throw new Error("Repository not initialized. Call initialize() first.");
348
+ }
349
+ await this.repo.addRemote(name, url);
350
+ return {
351
+ success: true,
352
+ message: `Remote '${name}' added successfully`,
353
+ name,
354
+ url
355
+ };
356
+ } catch (error) {
357
+ return {
358
+ success: false,
359
+ message: `Failed to add remote '${name}': ${error.message}`
360
+ };
361
+ }
362
+ }
363
+ /**
364
+ * Remove a remote repository
365
+ * @param name - Remote name to remove
366
+ * @returns Result of the operation
367
+ */
368
+ async removeRemote(name) {
369
+ try {
370
+ if (!this.repo) {
371
+ throw new Error("Repository not initialized. Call initialize() first.");
372
+ }
373
+ await this.repo.removeRemote(name);
374
+ return {
375
+ success: true,
376
+ message: `Remote '${name}' removed successfully`,
377
+ name
378
+ };
379
+ } catch (error) {
380
+ return {
381
+ success: false,
382
+ message: `Failed to remove remote '${name}': ${error.message}`
383
+ };
384
+ }
385
+ }
386
+ /**
387
+ * List all remote repositories
388
+ * @returns Result containing the list of remotes
389
+ */
390
+ async listRemotes() {
391
+ try {
392
+ if (!this.repo) {
393
+ throw new Error("Repository not initialized. Call initialize() first.");
394
+ }
395
+ const remotes = await this.repo.getRemotes(true);
396
+ return {
397
+ success: true,
398
+ remotes: remotes.map((remote) => ({
399
+ name: remote.name,
400
+ refs: remote.refs.fetch,
401
+ pushUrl: remote.refs.push
402
+ }))
403
+ };
404
+ } catch (error) {
405
+ return {
406
+ success: false,
407
+ message: `Failed to list remotes: ${error.message}`,
408
+ remotes: []
409
+ };
410
+ }
411
+ }
412
+ /**
413
+ * Push changes to a remote repository
414
+ * @param remote - Remote name (defaults to 'origin')
415
+ * @param branch - Branch to push (defaults to current branch)
416
+ * @param options - Additional options
417
+ * @returns Result of the operation
418
+ */
419
+ async push(remote = "origin", branch = "main", options = {}) {
420
+ try {
421
+ if (!this.repo) {
422
+ throw new Error("Repository not initialized. Call initialize() first.");
423
+ }
424
+ const remotes = await this.listRemotes();
425
+ const remoteExists = remotes.remotes.some((r) => r.name === remote);
426
+ if (!remoteExists) {
427
+ return {
428
+ success: false,
429
+ message: `Remote '${remote}' does not exist`
430
+ };
431
+ }
432
+ if (!branch || branch === "current") {
433
+ try {
434
+ const status = await this.repo.status();
435
+ branch = status.current || "main";
436
+ } catch {
437
+ branch = "main";
438
+ }
439
+ }
440
+ const pushOptions = {};
441
+ if (options.force) {
442
+ pushOptions["--force"] = null;
443
+ }
444
+ if (options.setUpstream !== false) {
445
+ pushOptions["-u"] = null;
446
+ }
447
+ await this.repo.push(remote, branch, pushOptions);
448
+ return {
449
+ success: true,
450
+ message: `Successfully pushed to ${remote}/${branch}`,
451
+ remote,
452
+ branch
453
+ };
454
+ } catch (error) {
455
+ if (error.message.includes("could not find remote") || error.message.includes("does not appear to be a git repository")) {
456
+ return {
457
+ success: false,
458
+ message: `Remote '${remote}' does not exist or is not a valid git repository`
459
+ };
460
+ }
461
+ if (error.message.includes("authentication failed")) {
462
+ return {
463
+ success: false,
464
+ message: `Authentication failed. Please check your credentials.`
465
+ };
466
+ }
467
+ return {
468
+ success: false,
469
+ message: `Failed to push to ${remote}/${branch}: ${error.message}`
470
+ };
471
+ }
472
+ }
473
+ /**
474
+ * Pull changes from a remote repository
475
+ * @param remote - Remote name (defaults to 'origin')
476
+ * @param branch - Branch to pull from (defaults to current branch)
477
+ * @param options - Additional options
478
+ * @returns Result of the operation
479
+ */
480
+ async pull(remote = "origin", branch = "main", options = {}) {
481
+ try {
482
+ if (!this.repo) {
483
+ throw new Error("Repository not initialized. Call initialize() first.");
484
+ }
485
+ const remotes = await this.listRemotes();
486
+ const remoteExists = remotes.remotes.some((r) => r.name === remote);
487
+ if (!remoteExists) {
488
+ return {
489
+ success: false,
490
+ message: `Remote '${remote}' does not exist`
491
+ };
492
+ }
493
+ if (!branch || branch === "current") {
494
+ try {
495
+ const status = await this.repo.status();
496
+ branch = status.current || "main";
497
+ } catch {
498
+ branch = "main";
499
+ }
500
+ }
501
+ const pullOptions = [];
502
+ if (options.allowUnrelatedHistories) {
503
+ pullOptions.push("--allow-unrelated-histories");
504
+ }
505
+ await this.repo.pull(remote, branch, pullOptions);
506
+ return {
507
+ success: true,
508
+ message: `Successfully pulled from ${remote}/${branch}`,
509
+ remote,
510
+ branch
511
+ };
512
+ } catch (error) {
513
+ if (error.message.includes("could not find remote")) {
514
+ return {
515
+ success: false,
516
+ message: `Remote '${remote}' does not exist`
517
+ };
518
+ }
519
+ if (error.message.includes("authentication failed")) {
520
+ return {
521
+ success: false,
522
+ message: `Authentication failed. Please check your credentials.`
523
+ };
524
+ }
525
+ if (error.message.includes("conflict")) {
526
+ return {
527
+ success: false,
528
+ message: `Merge conflict occurred. Please resolve conflicts manually.`
529
+ };
530
+ }
531
+ return {
532
+ success: false,
533
+ message: `Failed to pull from ${remote}/${branch}: ${error.message}`
534
+ };
535
+ }
536
+ }
537
+ /**
538
+ * Fetch changes from a remote repository without merging
539
+ * @param remote - Remote name (defaults to 'origin')
540
+ * @param branch - Branch to fetch (defaults to 'main')
541
+ * @param options - Additional options
542
+ * @returns Result of the operation
543
+ */
544
+ async fetch(remote = "origin", branch = "main", options = {}) {
545
+ try {
546
+ if (!this.repo) {
547
+ throw new Error("Repository not initialized. Call initialize() first.");
548
+ }
549
+ const remotes = await this.listRemotes();
550
+ const remoteExists = remotes.remotes.some((r) => r.name === remote);
551
+ if (!remoteExists) {
552
+ return {
553
+ success: false,
554
+ message: `Remote '${remote}' does not exist`
555
+ };
556
+ }
557
+ const fetchOptions = {};
558
+ if (options.prune) {
559
+ fetchOptions["--prune"] = null;
560
+ }
561
+ await this.repo.fetch(remote, branch, fetchOptions);
562
+ return {
563
+ success: true,
564
+ message: `Successfully fetched from ${remote}/${branch}`,
565
+ remote,
566
+ branch
567
+ };
568
+ } catch (error) {
569
+ if (error.message.includes("could not find remote")) {
570
+ return {
571
+ success: false,
572
+ message: `Remote '${remote}' does not exist`
573
+ };
574
+ }
575
+ if (error.message.includes("authentication failed")) {
576
+ return {
577
+ success: false,
578
+ message: `Authentication failed. Please check your credentials.`
579
+ };
580
+ }
581
+ return {
582
+ success: false,
583
+ message: `Failed to fetch from ${remote}/${branch}: ${error.message}`
584
+ };
585
+ }
586
+ }
587
+ /**
588
+ * Clone a remote repository
589
+ * @param url - Repository URL to clone
590
+ * @param targetPath - Directory to clone into (optional)
591
+ * @param options - Additional options
592
+ * @returns Result of the operation
593
+ */
594
+ async clone(url, targetPath = null, options = {}) {
595
+ try {
596
+ const clonePath = targetPath || this.repoPath;
597
+ const cloneOptions = {};
598
+ if (options.bare) {
599
+ cloneOptions["--bare"] = null;
600
+ }
601
+ if (options.branch) {
602
+ cloneOptions["--branch"] = options.branch;
603
+ }
604
+ if (options.depth) {
605
+ cloneOptions["--depth"] = options.depth.toString();
606
+ }
607
+ const git = simpleGit();
608
+ await git.clone(url, clonePath, cloneOptions);
609
+ if (!targetPath) {
610
+ this.repoPath = clonePath;
611
+ this.repo = simpleGit(clonePath);
612
+ }
613
+ return {
614
+ success: true,
615
+ message: `Repository cloned successfully from ${url}`,
616
+ url,
617
+ path: clonePath
618
+ };
619
+ } catch (error) {
620
+ return {
621
+ success: false,
622
+ message: `Failed to clone repository from ${url}: ${error.message}`
623
+ };
624
+ }
625
+ }
626
+ listFiles() {
627
+ return new Promise((resolve) => {
628
+ try {
629
+ const files = [];
630
+ const traverse = (dir, relativePath = "") => {
631
+ const items = fs.readdirSync(dir);
632
+ items.forEach((item) => {
633
+ const itemPath = path.join(dir, item);
634
+ const itemRelativePath = path.join(relativePath, item);
635
+ if (fs.statSync(itemPath).isDirectory()) {
636
+ traverse(itemPath, itemRelativePath);
637
+ } else {
638
+ files.push(itemRelativePath);
639
+ }
640
+ });
641
+ };
642
+ traverse(this.repoPath);
643
+ resolve(files.filter((file) => !file.startsWith(".git")));
644
+ } catch (error) {
645
+ resolve([]);
646
+ }
647
+ });
648
+ }
649
+ };
650
+ var index_default = Storage;
651
+ export {
652
+ Storage,
653
+ index_default as default
654
+ };
655
+ //# sourceMappingURL=index.mjs.map