@interactive-inc/docs-client 1.0.19 → 1.1.0

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,1002 @@
1
+ // lib/modules/path-system/doc-path-system.ts
2
+ var DocPathSystem = class {
3
+ separator = "/";
4
+ /**
5
+ * Join paths
6
+ */
7
+ join(...paths) {
8
+ const parts = [];
9
+ for (const path of paths) {
10
+ if (path === "") continue;
11
+ const segments = path.split(this.separator).filter(Boolean);
12
+ parts.push(...segments);
13
+ }
14
+ if (parts.length === 0) {
15
+ return ".";
16
+ }
17
+ const isAbsolute = paths[0]?.startsWith(this.separator);
18
+ const joined = parts.join(this.separator);
19
+ return isAbsolute ? `${this.separator}${joined}` : joined;
20
+ }
21
+ /**
22
+ * Get filename with extension
23
+ */
24
+ basename(path, ext) {
25
+ const segments = path.split(this.separator);
26
+ const filename = segments[segments.length - 1] || "";
27
+ if (ext && filename.endsWith(ext)) {
28
+ return filename.slice(0, -ext.length);
29
+ }
30
+ return filename;
31
+ }
32
+ /**
33
+ * Get directory name
34
+ */
35
+ dirname(path) {
36
+ const segments = path.split(this.separator);
37
+ if (segments.length <= 1) {
38
+ return ".";
39
+ }
40
+ segments.pop();
41
+ const dir = segments.join(this.separator);
42
+ return dir || ".";
43
+ }
44
+ /**
45
+ * Get extension
46
+ */
47
+ extname(path) {
48
+ const basename = this.basename(path);
49
+ const lastDot = basename.lastIndexOf(".");
50
+ if (lastDot <= 0) {
51
+ return "";
52
+ }
53
+ return basename.slice(lastDot);
54
+ }
55
+ /**
56
+ * Calculate relative path
57
+ */
58
+ relative(from, to) {
59
+ const fromSegments = from.split(this.separator).filter(Boolean);
60
+ const toSegments = to.split(this.separator).filter(Boolean);
61
+ let commonLength = 0;
62
+ for (let i = 0; i < Math.min(fromSegments.length, toSegments.length); i++) {
63
+ if (fromSegments[i] === toSegments[i]) {
64
+ commonLength++;
65
+ } else {
66
+ break;
67
+ }
68
+ }
69
+ const upCount = fromSegments.length - commonLength;
70
+ const upSegments = Array(upCount).fill("..");
71
+ const downSegments = toSegments.slice(commonLength);
72
+ const segments = [...upSegments, ...downSegments];
73
+ return segments.length > 0 ? segments.join(this.separator) : ".";
74
+ }
75
+ /**
76
+ * Convert to absolute path
77
+ */
78
+ resolve(...paths) {
79
+ let resolved = "";
80
+ for (const path of paths) {
81
+ if (path.startsWith(this.separator)) {
82
+ resolved = path;
83
+ } else if (path !== "") {
84
+ resolved = resolved ? this.join(resolved, path) : path;
85
+ }
86
+ }
87
+ return resolved || ".";
88
+ }
89
+ /**
90
+ * Normalize path
91
+ */
92
+ normalize(path) {
93
+ const isAbsolute = path.startsWith(this.separator);
94
+ const segments = path.split(this.separator).filter(Boolean);
95
+ const normalized = [];
96
+ for (const segment of segments) {
97
+ if (segment === "..") {
98
+ if (normalized.length > 0 && normalized[normalized.length - 1] !== "..") {
99
+ normalized.pop();
100
+ } else if (!isAbsolute) {
101
+ normalized.push("..");
102
+ }
103
+ } else if (segment !== ".") {
104
+ normalized.push(segment);
105
+ }
106
+ }
107
+ if (normalized.length === 0) {
108
+ return isAbsolute ? this.separator : ".";
109
+ }
110
+ const result = normalized.join(this.separator);
111
+ return isAbsolute ? `${this.separator}${result}` : result;
112
+ }
113
+ /**
114
+ * Check if path is absolute
115
+ */
116
+ isAbsolute(path) {
117
+ return path.startsWith(this.separator);
118
+ }
119
+ };
120
+
121
+ // lib/modules/file-system/doc-file-system-read.ts
122
+ import * as fs from "fs/promises";
123
+ var DocFileSystemRead = class {
124
+ basePath;
125
+ pathSystem;
126
+ constructor(props) {
127
+ this.basePath = props.basePath;
128
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
129
+ }
130
+ /**
131
+ * Read file content at the specified path
132
+ */
133
+ async readFile(relativePath) {
134
+ try {
135
+ const exists = await this.exists(relativePath);
136
+ if (!exists) return null;
137
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
138
+ return await fs.readFile(fullPath, "utf-8");
139
+ } catch (error) {
140
+ return error instanceof Error ? error : new Error(`Failed to read file at ${relativePath}`);
141
+ }
142
+ }
143
+ /**
144
+ * Get file name from path
145
+ */
146
+ readFileName(relativePath) {
147
+ return this.pathSystem.basename(relativePath);
148
+ }
149
+ /**
150
+ * Get file extension from path
151
+ */
152
+ readFileExtension(relativePath) {
153
+ return this.pathSystem.extname(relativePath);
154
+ }
155
+ /**
156
+ * Get directory path where the file exists
157
+ */
158
+ readFileDirectory(relativePath) {
159
+ return this.pathSystem.dirname(relativePath);
160
+ }
161
+ /**
162
+ * Get list of entries in directory
163
+ */
164
+ async readDirectoryFileNames(relativePath = "") {
165
+ try {
166
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
167
+ return await fs.readdir(fullPath);
168
+ } catch (error) {
169
+ return error instanceof Error ? error : new Error(`Failed to read directory at ${relativePath}`);
170
+ }
171
+ }
172
+ async readDirectoryFilePaths(relativePath) {
173
+ try {
174
+ const fileNames = await this.readDirectoryFileNames(relativePath);
175
+ if (fileNames instanceof Error) {
176
+ return fileNames;
177
+ }
178
+ return fileNames.map(
179
+ (fileName) => this.pathSystem.join(relativePath, fileName)
180
+ );
181
+ } catch (error) {
182
+ return error instanceof Error ? error : new Error(`Failed to read directory file paths at ${relativePath}`);
183
+ }
184
+ }
185
+ /**
186
+ * Check if path is a directory
187
+ */
188
+ async isDirectory(relativePath) {
189
+ try {
190
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
191
+ const stats = await fs.stat(fullPath);
192
+ return stats.isDirectory();
193
+ } catch {
194
+ return false;
195
+ }
196
+ }
197
+ /**
198
+ * Check if path is a file
199
+ */
200
+ async isFile(relativePath) {
201
+ try {
202
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
203
+ const stats = await fs.stat(fullPath);
204
+ return stats.isFile();
205
+ } catch {
206
+ return false;
207
+ }
208
+ }
209
+ /**
210
+ * Check if file or directory exists
211
+ */
212
+ async exists(relativePath) {
213
+ try {
214
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
215
+ await fs.access(fullPath);
216
+ return true;
217
+ } catch {
218
+ return false;
219
+ }
220
+ }
221
+ /**
222
+ * Check if directory exists
223
+ */
224
+ async directoryExists(relativePath) {
225
+ return this.isDirectory(relativePath);
226
+ }
227
+ /**
228
+ * Check if file exists
229
+ */
230
+ async fileExists(relativePath) {
231
+ return this.isFile(relativePath);
232
+ }
233
+ /**
234
+ * Get base path
235
+ */
236
+ getBasePath() {
237
+ return this.basePath;
238
+ }
239
+ /**
240
+ * Convert relative path to absolute path
241
+ */
242
+ resolve(relativePath) {
243
+ return this.pathSystem.join(this.basePath, relativePath);
244
+ }
245
+ /**
246
+ * Get file size in bytes
247
+ */
248
+ async getFileSize(relativePath) {
249
+ try {
250
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
251
+ const stats = await fs.stat(fullPath);
252
+ return stats.size;
253
+ } catch (error) {
254
+ return error instanceof Error ? error : new Error(`Failed to get file size at ${relativePath}`);
255
+ }
256
+ }
257
+ /**
258
+ * Get file last modified time
259
+ */
260
+ async getFileModifiedTime(relativePath) {
261
+ try {
262
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
263
+ const stats = await fs.stat(fullPath);
264
+ return stats.mtime;
265
+ } catch (error) {
266
+ return error instanceof Error ? error : new Error(`Failed to get file modified time at ${relativePath}`);
267
+ }
268
+ }
269
+ /**
270
+ * Get file creation time
271
+ */
272
+ async getFileCreatedTime(relativePath) {
273
+ try {
274
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
275
+ const stats = await fs.stat(fullPath);
276
+ return stats.birthtime;
277
+ } catch (error) {
278
+ return error instanceof Error ? error : new Error(`Failed to get file created time at ${relativePath}`);
279
+ }
280
+ }
281
+ };
282
+
283
+ // lib/modules/file-system/doc-file-system-write.ts
284
+ import * as fs2 from "fs/promises";
285
+ var DocFileSystemWrite = class {
286
+ basePath;
287
+ pathSystem;
288
+ reader;
289
+ constructor(props) {
290
+ this.basePath = props.basePath;
291
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
292
+ this.reader = props.reader;
293
+ }
294
+ /**
295
+ * Write content to file at the specified path (creates directory if needed)
296
+ */
297
+ async writeFile(relativePath, content) {
298
+ try {
299
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
300
+ const dirPath = this.pathSystem.dirname(fullPath);
301
+ await fs2.mkdir(dirPath, { recursive: true });
302
+ await fs2.writeFile(fullPath, content, "utf-8");
303
+ return null;
304
+ } catch (error) {
305
+ return error instanceof Error ? error : new Error(`Failed to write file at ${relativePath}`);
306
+ }
307
+ }
308
+ /**
309
+ * Delete file at the specified path
310
+ */
311
+ async deleteFile(relativePath) {
312
+ try {
313
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
314
+ await fs2.unlink(fullPath);
315
+ return null;
316
+ } catch (error) {
317
+ return new Error(`Failed to delete file at ${relativePath}: ${error}`);
318
+ }
319
+ }
320
+ /**
321
+ * Create directory
322
+ */
323
+ async createDirectory(relativePath) {
324
+ try {
325
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
326
+ await fs2.mkdir(fullPath, { recursive: true });
327
+ return null;
328
+ } catch (error) {
329
+ return error instanceof Error ? error : new Error(`Failed to create directory at ${relativePath}`);
330
+ }
331
+ }
332
+ /**
333
+ * Create directory if it doesn't exist
334
+ */
335
+ async ensureDirectoryExists(relativePath) {
336
+ try {
337
+ if (this.reader) {
338
+ const exists = await this.reader.exists(relativePath);
339
+ if (exists) {
340
+ return null;
341
+ }
342
+ }
343
+ const result = await this.createDirectory(relativePath);
344
+ if (result instanceof Error) {
345
+ return result;
346
+ }
347
+ return null;
348
+ } catch (error) {
349
+ return error instanceof Error ? error : new Error(`Failed to ensure directory exists at ${relativePath}`);
350
+ }
351
+ }
352
+ /**
353
+ * Copy file
354
+ */
355
+ async copyFile(sourcePath, destinationPath) {
356
+ try {
357
+ const sourceFullPath = this.pathSystem.join(this.basePath, sourcePath);
358
+ const destFullPath = this.pathSystem.join(this.basePath, destinationPath);
359
+ await fs2.copyFile(sourceFullPath, destFullPath);
360
+ return null;
361
+ } catch (error) {
362
+ return error instanceof Error ? error : new Error(
363
+ `Failed to copy file from ${sourcePath} to ${destinationPath}`
364
+ );
365
+ }
366
+ }
367
+ /**
368
+ * Move file
369
+ */
370
+ async moveFile(sourcePath, destinationPath) {
371
+ try {
372
+ const sourceFullPath = this.pathSystem.join(this.basePath, sourcePath);
373
+ const destFullPath = this.pathSystem.join(this.basePath, destinationPath);
374
+ await fs2.rename(sourceFullPath, destFullPath);
375
+ return null;
376
+ } catch (error) {
377
+ return error instanceof Error ? error : new Error(
378
+ `Failed to move file from ${sourcePath} to ${destinationPath}`
379
+ );
380
+ }
381
+ }
382
+ };
383
+
384
+ // lib/modules/file-system/doc-file-system.ts
385
+ var DocFileSystem = class {
386
+ reader;
387
+ writer;
388
+ basePath;
389
+ pathSystem;
390
+ constructor(props) {
391
+ this.basePath = props.basePath;
392
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
393
+ this.reader = props.reader ?? new DocFileSystemRead({
394
+ basePath: this.basePath,
395
+ pathSystem: this.pathSystem
396
+ });
397
+ this.writer = props.writer ?? new DocFileSystemWrite({
398
+ basePath: this.basePath,
399
+ pathSystem: this.pathSystem,
400
+ reader: this.reader
401
+ });
402
+ }
403
+ /**
404
+ * Read file content at the specified path
405
+ */
406
+ async readFile(relativePath) {
407
+ return this.reader.readFile(relativePath);
408
+ }
409
+ /**
410
+ * Write content to file at the specified path (creates directory if needed)
411
+ */
412
+ async writeFile(relativePath, content) {
413
+ return this.writer.writeFile(relativePath, content);
414
+ }
415
+ /**
416
+ * Delete file at the specified path
417
+ */
418
+ async deleteFile(relativePath) {
419
+ return this.writer.deleteFile(relativePath);
420
+ }
421
+ /**
422
+ * Get file name from path
423
+ */
424
+ readFileName(relativePath) {
425
+ return this.reader.readFileName(relativePath);
426
+ }
427
+ /**
428
+ * Get file extension from path
429
+ */
430
+ readFileExtension(relativePath) {
431
+ return this.reader.readFileExtension(relativePath);
432
+ }
433
+ /**
434
+ * Get directory path where the file exists
435
+ */
436
+ readFileDirectory(relativePath) {
437
+ return this.reader.readFileDirectory(relativePath);
438
+ }
439
+ /**
440
+ * Get list of entries in directory
441
+ */
442
+ async readDirectoryFileNames(relativePath = "") {
443
+ return this.reader.readDirectoryFileNames(relativePath);
444
+ }
445
+ async readDirectoryFilePaths(relativePath) {
446
+ return this.reader.readDirectoryFilePaths(relativePath);
447
+ }
448
+ /**
449
+ * Check if path is a directory
450
+ */
451
+ async isDirectory(relativePath) {
452
+ return this.reader.isDirectory(relativePath);
453
+ }
454
+ /**
455
+ * Check if path is a file
456
+ */
457
+ async isFile(relativePath) {
458
+ return this.reader.isFile(relativePath);
459
+ }
460
+ /**
461
+ * Check if file or directory exists
462
+ */
463
+ async exists(relativePath) {
464
+ return this.reader.exists(relativePath);
465
+ }
466
+ /**
467
+ * Check if directory exists
468
+ */
469
+ async directoryExists(relativePath) {
470
+ return this.reader.directoryExists(relativePath);
471
+ }
472
+ /**
473
+ * Check if file exists
474
+ */
475
+ async fileExists(relativePath) {
476
+ return this.reader.fileExists(relativePath);
477
+ }
478
+ /**
479
+ * Get base path
480
+ */
481
+ getBasePath() {
482
+ return this.reader.getBasePath();
483
+ }
484
+ /**
485
+ * Convert relative path to absolute path
486
+ */
487
+ resolve(relativePath) {
488
+ return this.reader.resolve(relativePath);
489
+ }
490
+ /**
491
+ * Create directory
492
+ */
493
+ async createDirectory(relativePath) {
494
+ return this.writer.createDirectory(relativePath);
495
+ }
496
+ /**
497
+ * Get file size in bytes
498
+ */
499
+ async getFileSize(relativePath) {
500
+ return this.reader.getFileSize(relativePath);
501
+ }
502
+ /**
503
+ * Create directory if it doesn't exist
504
+ */
505
+ async ensureDirectoryExists(relativePath) {
506
+ return this.writer.ensureDirectoryExists(relativePath);
507
+ }
508
+ /**
509
+ * Copy file
510
+ */
511
+ async copyFile(sourcePath, destinationPath) {
512
+ return this.writer.copyFile(sourcePath, destinationPath);
513
+ }
514
+ /**
515
+ * Move file
516
+ */
517
+ async moveFile(sourcePath, destinationPath) {
518
+ return this.writer.moveFile(sourcePath, destinationPath);
519
+ }
520
+ /**
521
+ * Get file last modified time
522
+ */
523
+ async getFileModifiedTime(relativePath) {
524
+ return this.reader.getFileModifiedTime(relativePath);
525
+ }
526
+ /**
527
+ * Get file creation time
528
+ */
529
+ async getFileCreatedTime(relativePath) {
530
+ return this.reader.getFileCreatedTime(relativePath);
531
+ }
532
+ };
533
+
534
+ // lib/modules/file-system/doc-file-system-octokit-read.ts
535
+ import { Octokit } from "@octokit/rest";
536
+ var DocFileSystemOctokitRead = class {
537
+ octokit;
538
+ owner;
539
+ repo;
540
+ branch;
541
+ pathSystem;
542
+ basePath;
543
+ fileCache = /* @__PURE__ */ new Map();
544
+ constructor(props) {
545
+ this.owner = props.owner;
546
+ this.repo = props.repo;
547
+ this.branch = props.branch ?? "main";
548
+ this.basePath = props.basePath;
549
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
550
+ this.octokit = new Octokit({ auth: props.token });
551
+ Object.freeze(this);
552
+ }
553
+ /**
554
+ * Read file content from GitHub repository
555
+ */
556
+ async readFile(relativePath) {
557
+ try {
558
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
559
+ const response = await this.octokit.repos.getContent({
560
+ owner: this.owner,
561
+ repo: this.repo,
562
+ path: fullPath,
563
+ ref: this.branch
564
+ });
565
+ if ("type" in response.data && response.data.type === "file") {
566
+ const content = Buffer.from(response.data.content, "base64").toString(
567
+ "utf-8"
568
+ );
569
+ this.fileCache.set(fullPath, {
570
+ sha: response.data.sha,
571
+ content,
572
+ size: response.data.size
573
+ });
574
+ return content;
575
+ }
576
+ return new Error(`Path ${relativePath} is not a file`);
577
+ } catch (error) {
578
+ if (error && typeof error === "object" && "status" in error && error.status === 404) {
579
+ return null;
580
+ }
581
+ return error instanceof Error ? error : new Error(`Failed to read file at ${relativePath}`);
582
+ }
583
+ }
584
+ /**
585
+ * Get file name from path
586
+ */
587
+ readFileName(relativePath) {
588
+ return this.pathSystem.basename(relativePath);
589
+ }
590
+ /**
591
+ * Get file extension from path
592
+ */
593
+ readFileExtension(relativePath) {
594
+ return this.pathSystem.extname(relativePath);
595
+ }
596
+ /**
597
+ * Get directory path where the file exists
598
+ */
599
+ readFileDirectory(relativePath) {
600
+ return this.pathSystem.dirname(relativePath);
601
+ }
602
+ /**
603
+ * Get list of file names in directory
604
+ */
605
+ async readDirectoryFileNames(relativePath = "") {
606
+ try {
607
+ const fullPath = relativePath ? this.pathSystem.join(this.basePath, relativePath) : this.basePath;
608
+ const response = await this.octokit.repos.getContent({
609
+ owner: this.owner,
610
+ repo: this.repo,
611
+ path: fullPath,
612
+ ref: this.branch
613
+ });
614
+ if (Array.isArray(response.data)) {
615
+ return response.data.map((item) => item.name);
616
+ }
617
+ return new Error(`Path ${relativePath} is not a directory`);
618
+ } catch (error) {
619
+ return error instanceof Error ? error : new Error(`Failed to read directory at ${relativePath}`);
620
+ }
621
+ }
622
+ /**
623
+ * Get list of file paths in directory
624
+ */
625
+ async readDirectoryFilePaths(relativePath) {
626
+ try {
627
+ const fileNames = await this.readDirectoryFileNames(relativePath);
628
+ if (fileNames instanceof Error) {
629
+ return fileNames;
630
+ }
631
+ return fileNames.map(
632
+ (fileName) => this.pathSystem.join(relativePath, fileName)
633
+ );
634
+ } catch (error) {
635
+ return error instanceof Error ? error : new Error(`Failed to read directory file paths at ${relativePath}`);
636
+ }
637
+ }
638
+ /**
639
+ * Check if path is a directory
640
+ */
641
+ async isDirectory(relativePath) {
642
+ try {
643
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
644
+ const response = await this.octokit.repos.getContent({
645
+ owner: this.owner,
646
+ repo: this.repo,
647
+ path: fullPath,
648
+ ref: this.branch
649
+ });
650
+ return Array.isArray(response.data);
651
+ } catch {
652
+ return false;
653
+ }
654
+ }
655
+ /**
656
+ * Check if path is a file
657
+ */
658
+ async isFile(relativePath) {
659
+ try {
660
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
661
+ const response = await this.octokit.repos.getContent({
662
+ owner: this.owner,
663
+ repo: this.repo,
664
+ path: fullPath,
665
+ ref: this.branch
666
+ });
667
+ return !Array.isArray(response.data) && response.data.type === "file";
668
+ } catch {
669
+ return false;
670
+ }
671
+ }
672
+ /**
673
+ * Check if file or directory exists
674
+ */
675
+ async exists(relativePath) {
676
+ try {
677
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
678
+ await this.octokit.repos.getContent({
679
+ owner: this.owner,
680
+ repo: this.repo,
681
+ path: fullPath,
682
+ ref: this.branch
683
+ });
684
+ return true;
685
+ } catch {
686
+ return false;
687
+ }
688
+ }
689
+ /**
690
+ * Check if directory exists
691
+ */
692
+ async directoryExists(relativePath) {
693
+ return this.isDirectory(relativePath);
694
+ }
695
+ /**
696
+ * Check if file exists
697
+ */
698
+ async fileExists(relativePath) {
699
+ return this.isFile(relativePath);
700
+ }
701
+ /**
702
+ * Get base path
703
+ */
704
+ getBasePath() {
705
+ return this.basePath;
706
+ }
707
+ /**
708
+ * Convert relative path to absolute path
709
+ */
710
+ resolve(relativePath) {
711
+ return this.pathSystem.join(this.basePath, relativePath);
712
+ }
713
+ /**
714
+ * Get file size in bytes
715
+ */
716
+ async getFileSize(relativePath) {
717
+ try {
718
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
719
+ const cached = this.fileCache.get(fullPath);
720
+ if (cached) {
721
+ return cached.size;
722
+ }
723
+ const response = await this.octokit.repos.getContent({
724
+ owner: this.owner,
725
+ repo: this.repo,
726
+ path: fullPath,
727
+ ref: this.branch
728
+ });
729
+ if ("size" in response.data) {
730
+ return response.data.size;
731
+ }
732
+ return new Error(`Cannot get size for ${relativePath}`);
733
+ } catch (error) {
734
+ return error instanceof Error ? error : new Error(`Failed to get file size at ${relativePath}`);
735
+ }
736
+ }
737
+ /**
738
+ * Get file last modified time
739
+ */
740
+ async getFileModifiedTime(relativePath) {
741
+ try {
742
+ const commits = await this.octokit.repos.listCommits({
743
+ owner: this.owner,
744
+ repo: this.repo,
745
+ path: relativePath,
746
+ sha: this.branch,
747
+ per_page: 1
748
+ });
749
+ if (commits.data.length > 0 && commits.data[0].commit.author?.date) {
750
+ return new Date(commits.data[0].commit.author.date);
751
+ }
752
+ return new Error(`No commits found for ${relativePath}`);
753
+ } catch (error) {
754
+ return error instanceof Error ? error : new Error(`Failed to get file modified time at ${relativePath}`);
755
+ }
756
+ }
757
+ /**
758
+ * Get file creation time (first commit)
759
+ */
760
+ async getFileCreatedTime(relativePath) {
761
+ try {
762
+ const commits = await this.octokit.repos.listCommits({
763
+ owner: this.owner,
764
+ repo: this.repo,
765
+ path: relativePath,
766
+ sha: this.branch
767
+ });
768
+ const lastCommit = commits.data[commits.data.length - 1];
769
+ if (lastCommit?.commit.author?.date) {
770
+ return new Date(lastCommit.commit.author.date);
771
+ }
772
+ return new Error(`No commits found for ${relativePath}`);
773
+ } catch (error) {
774
+ return error instanceof Error ? error : new Error(`Failed to get file created time at ${relativePath}`);
775
+ }
776
+ }
777
+ /**
778
+ * Get cached SHA for a file
779
+ */
780
+ getCachedSha(relativePath) {
781
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
782
+ return this.fileCache.get(fullPath)?.sha;
783
+ }
784
+ };
785
+
786
+ // lib/modules/file-system/doc-file-system-octokit-write.ts
787
+ import { Octokit as Octokit2 } from "@octokit/rest";
788
+ var DocFileSystemOctokitWrite = class {
789
+ octokit;
790
+ owner;
791
+ repo;
792
+ basePath;
793
+ branch;
794
+ pathSystem;
795
+ reader;
796
+ constructor(props) {
797
+ this.owner = props.owner;
798
+ this.repo = props.repo;
799
+ this.basePath = props.basePath;
800
+ this.branch = props.branch ?? "main";
801
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
802
+ this.octokit = new Octokit2({ auth: props.token });
803
+ this.reader = props.reader;
804
+ Object.freeze(this);
805
+ }
806
+ /**
807
+ * Write content to file in GitHub repository
808
+ */
809
+ async writeFile(relativePath, content) {
810
+ try {
811
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
812
+ const encodedContent = Buffer.from(content).toString("base64");
813
+ let sha;
814
+ if (this.reader && "getCachedSha" in this.reader) {
815
+ sha = this.reader.getCachedSha(
816
+ relativePath
817
+ );
818
+ }
819
+ if (!sha) {
820
+ try {
821
+ const existing = await this.octokit.repos.getContent({
822
+ owner: this.owner,
823
+ repo: this.repo,
824
+ path: fullPath,
825
+ ref: this.branch
826
+ });
827
+ if ("sha" in existing.data) {
828
+ sha = existing.data.sha;
829
+ }
830
+ } catch {
831
+ }
832
+ }
833
+ await this.octokit.repos.createOrUpdateFileContents({
834
+ owner: this.owner,
835
+ repo: this.repo,
836
+ path: fullPath,
837
+ message: `Update ${fullPath}`,
838
+ content: encodedContent,
839
+ branch: this.branch,
840
+ sha
841
+ });
842
+ return null;
843
+ } catch (error) {
844
+ return error instanceof Error ? error : new Error(`Failed to write file at ${relativePath}`);
845
+ }
846
+ }
847
+ /**
848
+ * Delete file from GitHub repository
849
+ */
850
+ async deleteFile(relativePath) {
851
+ try {
852
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
853
+ let sha;
854
+ if (this.reader && "getCachedSha" in this.reader) {
855
+ sha = this.reader.getCachedSha(
856
+ relativePath
857
+ );
858
+ }
859
+ if (!sha) {
860
+ const existing = await this.octokit.repos.getContent({
861
+ owner: this.owner,
862
+ repo: this.repo,
863
+ path: fullPath,
864
+ ref: this.branch
865
+ });
866
+ if ("sha" in existing.data) {
867
+ sha = existing.data.sha;
868
+ }
869
+ }
870
+ if (!sha) {
871
+ return new Error(`Cannot delete file at ${relativePath}: SHA not found`);
872
+ }
873
+ await this.octokit.repos.deleteFile({
874
+ owner: this.owner,
875
+ repo: this.repo,
876
+ path: fullPath,
877
+ message: `Delete ${fullPath}`,
878
+ sha,
879
+ branch: this.branch
880
+ });
881
+ return null;
882
+ } catch (error) {
883
+ return error instanceof Error ? error : new Error(`Failed to delete file at ${relativePath}`);
884
+ }
885
+ }
886
+ /**
887
+ * Create directory by creating a .gitkeep file
888
+ */
889
+ async createDirectory(relativePath) {
890
+ try {
891
+ const gitkeepPath = this.pathSystem.join(relativePath, ".gitkeep");
892
+ return await this.writeFile(gitkeepPath, "");
893
+ } catch (error) {
894
+ return error instanceof Error ? error : new Error(`Failed to create directory at ${relativePath}`);
895
+ }
896
+ }
897
+ /**
898
+ * Create directory if it doesn't exist
899
+ */
900
+ async ensureDirectoryExists(relativePath) {
901
+ try {
902
+ if (this.reader) {
903
+ const exists = await this.reader.exists(relativePath);
904
+ if (exists) {
905
+ return null;
906
+ }
907
+ }
908
+ return await this.createDirectory(relativePath);
909
+ } catch (error) {
910
+ return error instanceof Error ? error : new Error(`Failed to ensure directory exists at ${relativePath}`);
911
+ }
912
+ }
913
+ /**
914
+ * Copy file to new location
915
+ */
916
+ async copyFile(sourcePath, destinationPath) {
917
+ try {
918
+ if (!this.reader) {
919
+ return new Error("Reader is required for copy operation");
920
+ }
921
+ const content = await this.reader.readFile(sourcePath);
922
+ if (content instanceof Error) {
923
+ return content;
924
+ }
925
+ if (content === null) {
926
+ return new Error(`Source file ${sourcePath} does not exist`);
927
+ }
928
+ return await this.writeFile(destinationPath, content);
929
+ } catch (error) {
930
+ return error instanceof Error ? error : new Error(
931
+ `Failed to copy file from ${sourcePath} to ${destinationPath}`
932
+ );
933
+ }
934
+ }
935
+ /**
936
+ * Move file to new location
937
+ */
938
+ async moveFile(sourcePath, destinationPath) {
939
+ try {
940
+ const copyResult = await this.copyFile(sourcePath, destinationPath);
941
+ if (copyResult !== null) {
942
+ return copyResult;
943
+ }
944
+ return await this.deleteFile(sourcePath);
945
+ } catch (error) {
946
+ return error instanceof Error ? error : new Error(
947
+ `Failed to move file from ${sourcePath} to ${destinationPath}`
948
+ );
949
+ }
950
+ }
951
+ };
952
+
953
+ // lib/modules/file-system/doc-file-system-octokit.ts
954
+ var DocFileSystemOctokit = class extends DocFileSystem {
955
+ owner;
956
+ repo;
957
+ branch;
958
+ constructor(props) {
959
+ const reader = new DocFileSystemOctokitRead({
960
+ owner: props.owner,
961
+ repo: props.repo,
962
+ basePath: props.basePath,
963
+ branch: props.branch,
964
+ token: props.token,
965
+ pathSystem: props.pathSystem
966
+ });
967
+ const writer = new DocFileSystemOctokitWrite({
968
+ owner: props.owner,
969
+ repo: props.repo,
970
+ basePath: props.basePath,
971
+ branch: props.branch,
972
+ token: props.token,
973
+ pathSystem: props.pathSystem,
974
+ reader
975
+ });
976
+ super({
977
+ basePath: props.basePath,
978
+ pathSystem: props.pathSystem,
979
+ reader,
980
+ writer
981
+ });
982
+ this.owner = props.owner;
983
+ this.repo = props.repo;
984
+ this.branch = props.branch ?? "main";
985
+ Object.freeze(this);
986
+ }
987
+ /**
988
+ * Get repository information
989
+ */
990
+ getRepositoryInfo() {
991
+ return {
992
+ owner: this.owner,
993
+ repo: this.repo,
994
+ branch: this.branch
995
+ };
996
+ }
997
+ };
998
+ export {
999
+ DocFileSystemOctokit,
1000
+ DocFileSystemOctokitRead,
1001
+ DocFileSystemOctokitWrite
1002
+ };