@interactive-inc/docs-client 1.0.20 → 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.
@@ -0,0 +1,715 @@
1
+ import { Octokit } from '@octokit/rest';
2
+
3
+ // lib/modules/path-system/doc-path-system.ts
4
+ var DocPathSystem = class {
5
+ separator = "/";
6
+ /**
7
+ * Join paths
8
+ */
9
+ join(...paths) {
10
+ const parts = [];
11
+ for (const path of paths) {
12
+ if (path === "") continue;
13
+ const segments = path.split(this.separator).filter(Boolean);
14
+ parts.push(...segments);
15
+ }
16
+ if (parts.length === 0) {
17
+ return ".";
18
+ }
19
+ const isAbsolute = paths[0]?.startsWith(this.separator);
20
+ const joined = parts.join(this.separator);
21
+ return isAbsolute ? `${this.separator}${joined}` : joined;
22
+ }
23
+ /**
24
+ * Get filename with extension
25
+ */
26
+ basename(path, ext) {
27
+ const segments = path.split(this.separator);
28
+ const filename = segments[segments.length - 1] || "";
29
+ if (ext && filename.endsWith(ext)) {
30
+ return filename.slice(0, -ext.length);
31
+ }
32
+ return filename;
33
+ }
34
+ /**
35
+ * Get directory name
36
+ */
37
+ dirname(path) {
38
+ const segments = path.split(this.separator);
39
+ if (segments.length <= 1) {
40
+ return ".";
41
+ }
42
+ segments.pop();
43
+ const dir = segments.join(this.separator);
44
+ return dir || ".";
45
+ }
46
+ /**
47
+ * Get extension
48
+ */
49
+ extname(path) {
50
+ const basename = this.basename(path);
51
+ const lastDot = basename.lastIndexOf(".");
52
+ if (lastDot <= 0) {
53
+ return "";
54
+ }
55
+ return basename.slice(lastDot);
56
+ }
57
+ /**
58
+ * Calculate relative path
59
+ */
60
+ relative(from, to) {
61
+ const fromSegments = from.split(this.separator).filter(Boolean);
62
+ const toSegments = to.split(this.separator).filter(Boolean);
63
+ let commonLength = 0;
64
+ for (let i = 0; i < Math.min(fromSegments.length, toSegments.length); i++) {
65
+ if (fromSegments[i] === toSegments[i]) {
66
+ commonLength++;
67
+ } else {
68
+ break;
69
+ }
70
+ }
71
+ const upCount = fromSegments.length - commonLength;
72
+ const upSegments = Array(upCount).fill("..");
73
+ const downSegments = toSegments.slice(commonLength);
74
+ const segments = [...upSegments, ...downSegments];
75
+ return segments.length > 0 ? segments.join(this.separator) : ".";
76
+ }
77
+ /**
78
+ * Convert to absolute path
79
+ */
80
+ resolve(...paths) {
81
+ let resolved = "";
82
+ for (const path of paths) {
83
+ if (path.startsWith(this.separator)) {
84
+ resolved = path;
85
+ } else if (path !== "") {
86
+ resolved = resolved ? this.join(resolved, path) : path;
87
+ }
88
+ }
89
+ return resolved || ".";
90
+ }
91
+ /**
92
+ * Normalize path
93
+ */
94
+ normalize(path) {
95
+ const isAbsolute = path.startsWith(this.separator);
96
+ const segments = path.split(this.separator).filter(Boolean);
97
+ const normalized = [];
98
+ for (const segment of segments) {
99
+ if (segment === "..") {
100
+ if (normalized.length > 0 && normalized[normalized.length - 1] !== "..") {
101
+ normalized.pop();
102
+ } else if (!isAbsolute) {
103
+ normalized.push("..");
104
+ }
105
+ } else if (segment !== ".") {
106
+ normalized.push(segment);
107
+ }
108
+ }
109
+ if (normalized.length === 0) {
110
+ return isAbsolute ? this.separator : ".";
111
+ }
112
+ const result = normalized.join(this.separator);
113
+ return isAbsolute ? `${this.separator}${result}` : result;
114
+ }
115
+ /**
116
+ * Check if path is absolute
117
+ */
118
+ isAbsolute(path) {
119
+ return path.startsWith(this.separator);
120
+ }
121
+ };
122
+
123
+ // lib/modules/file-system/doc-file-system.ts
124
+ var DocFileSystem = class {
125
+ reader;
126
+ writer;
127
+ basePath;
128
+ pathSystem;
129
+ constructor(props) {
130
+ this.basePath = props.basePath;
131
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
132
+ this.reader = props.reader;
133
+ this.writer = props.writer;
134
+ }
135
+ /**
136
+ * Read file content at the specified path
137
+ */
138
+ async readFile(relativePath) {
139
+ return this.reader.readFile(relativePath);
140
+ }
141
+ /**
142
+ * Write content to file at the specified path (creates directory if needed)
143
+ */
144
+ async writeFile(relativePath, content) {
145
+ return this.writer.writeFile(relativePath, content);
146
+ }
147
+ /**
148
+ * Delete file at the specified path
149
+ */
150
+ async deleteFile(relativePath) {
151
+ return this.writer.deleteFile(relativePath);
152
+ }
153
+ /**
154
+ * Get file name from path
155
+ */
156
+ readFileName(relativePath) {
157
+ return this.reader.readFileName(relativePath);
158
+ }
159
+ /**
160
+ * Get file extension from path
161
+ */
162
+ readFileExtension(relativePath) {
163
+ return this.reader.readFileExtension(relativePath);
164
+ }
165
+ /**
166
+ * Get directory path where the file exists
167
+ */
168
+ readFileDirectory(relativePath) {
169
+ return this.reader.readFileDirectory(relativePath);
170
+ }
171
+ /**
172
+ * Get list of entries in directory
173
+ */
174
+ async readDirectoryFileNames(relativePath = "") {
175
+ return this.reader.readDirectoryFileNames(relativePath);
176
+ }
177
+ async readDirectoryFilePaths(relativePath) {
178
+ return this.reader.readDirectoryFilePaths(relativePath);
179
+ }
180
+ /**
181
+ * Check if path is a directory
182
+ */
183
+ async isDirectory(relativePath) {
184
+ return this.reader.isDirectory(relativePath);
185
+ }
186
+ /**
187
+ * Check if path is a file
188
+ */
189
+ async isFile(relativePath) {
190
+ return this.reader.isFile(relativePath);
191
+ }
192
+ /**
193
+ * Check if file or directory exists
194
+ */
195
+ async exists(relativePath) {
196
+ return this.reader.exists(relativePath);
197
+ }
198
+ /**
199
+ * Check if directory exists
200
+ */
201
+ async directoryExists(relativePath) {
202
+ return this.reader.directoryExists(relativePath);
203
+ }
204
+ /**
205
+ * Check if file exists
206
+ */
207
+ async fileExists(relativePath) {
208
+ return this.reader.fileExists(relativePath);
209
+ }
210
+ /**
211
+ * Get base path
212
+ */
213
+ getBasePath() {
214
+ return this.reader.getBasePath();
215
+ }
216
+ /**
217
+ * Convert relative path to absolute path
218
+ */
219
+ resolve(relativePath) {
220
+ return this.reader.resolve(relativePath);
221
+ }
222
+ /**
223
+ * Create directory
224
+ */
225
+ async createEmptyDirectory(relativePath) {
226
+ return this.writer.createEmptyDirectory(relativePath);
227
+ }
228
+ /**
229
+ * Get file size in bytes
230
+ */
231
+ async getFileSize(relativePath) {
232
+ return this.reader.getFileSize(relativePath);
233
+ }
234
+ /**
235
+ * Create directory if it doesn't exist
236
+ */
237
+ async createDirectory(relativePath) {
238
+ return this.writer.createDirectory(relativePath);
239
+ }
240
+ /**
241
+ * Copy file
242
+ */
243
+ async copyFile(sourcePath, destinationPath) {
244
+ return this.writer.copyFile(sourcePath, destinationPath);
245
+ }
246
+ /**
247
+ * Move file
248
+ */
249
+ async moveFile(sourcePath, destinationPath) {
250
+ return this.writer.moveFile(sourcePath, destinationPath);
251
+ }
252
+ /**
253
+ * Get file last modified time
254
+ */
255
+ async getFileUpdatedTime(relativePath) {
256
+ return this.reader.getFileUpdatedTime(relativePath);
257
+ }
258
+ /**
259
+ * Get file creation time
260
+ */
261
+ async getFileCreatedTime(relativePath) {
262
+ return this.reader.getFileCreatedTime(relativePath);
263
+ }
264
+ };
265
+ var DocFileSystemOctokitRead = class {
266
+ octokit;
267
+ owner;
268
+ repo;
269
+ branch;
270
+ pathSystem;
271
+ basePath;
272
+ fileCache = /* @__PURE__ */ new Map();
273
+ constructor(props) {
274
+ this.owner = props.owner;
275
+ this.repo = props.repo;
276
+ this.branch = props.branch ?? "main";
277
+ this.basePath = props.basePath;
278
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
279
+ this.octokit = new Octokit({ auth: props.token });
280
+ Object.freeze(this);
281
+ }
282
+ /**
283
+ * Read file content from GitHub repository
284
+ */
285
+ async readFile(relativePath) {
286
+ try {
287
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
288
+ const response = await this.octokit.repos.getContent({
289
+ owner: this.owner,
290
+ repo: this.repo,
291
+ path: fullPath,
292
+ ref: this.branch
293
+ });
294
+ if ("type" in response.data && response.data.type === "file") {
295
+ const content = Buffer.from(response.data.content, "base64").toString(
296
+ "utf-8"
297
+ );
298
+ this.fileCache.set(fullPath, {
299
+ sha: response.data.sha,
300
+ content,
301
+ size: response.data.size
302
+ });
303
+ return content;
304
+ }
305
+ return new Error(`Path ${relativePath} is not a file`);
306
+ } catch (error) {
307
+ if (error && typeof error === "object" && "status" in error && error.status === 404) {
308
+ return null;
309
+ }
310
+ return error instanceof Error ? error : new Error(`Failed to read file at ${relativePath}`);
311
+ }
312
+ }
313
+ /**
314
+ * Get file name from path
315
+ */
316
+ readFileName(relativePath) {
317
+ return this.pathSystem.basename(relativePath);
318
+ }
319
+ /**
320
+ * Get file extension from path
321
+ */
322
+ readFileExtension(relativePath) {
323
+ return this.pathSystem.extname(relativePath);
324
+ }
325
+ /**
326
+ * Get directory path where the file exists
327
+ */
328
+ readFileDirectory(relativePath) {
329
+ return this.pathSystem.dirname(relativePath);
330
+ }
331
+ /**
332
+ * Get list of file names in directory
333
+ */
334
+ async readDirectoryFileNames(relativePath = "") {
335
+ try {
336
+ const fullPath = relativePath ? this.pathSystem.join(this.basePath, relativePath) : this.basePath;
337
+ const response = await this.octokit.repos.getContent({
338
+ owner: this.owner,
339
+ repo: this.repo,
340
+ path: fullPath,
341
+ ref: this.branch
342
+ });
343
+ if (Array.isArray(response.data)) {
344
+ return response.data.map((item) => item.name);
345
+ }
346
+ return new Error(`Path ${relativePath} is not a directory`);
347
+ } catch (error) {
348
+ return error instanceof Error ? error : new Error(`Failed to read directory at ${relativePath}`);
349
+ }
350
+ }
351
+ /**
352
+ * Get list of file paths in directory
353
+ */
354
+ async readDirectoryFilePaths(relativePath) {
355
+ try {
356
+ const fileNames = await this.readDirectoryFileNames(relativePath);
357
+ if (fileNames instanceof Error) {
358
+ return fileNames;
359
+ }
360
+ return fileNames.map(
361
+ (fileName) => this.pathSystem.join(relativePath, fileName)
362
+ );
363
+ } catch (error) {
364
+ return error instanceof Error ? error : new Error(`Failed to read directory file paths at ${relativePath}`);
365
+ }
366
+ }
367
+ /**
368
+ * Check if path is a directory
369
+ */
370
+ async isDirectory(relativePath) {
371
+ try {
372
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
373
+ const response = await this.octokit.repos.getContent({
374
+ owner: this.owner,
375
+ repo: this.repo,
376
+ path: fullPath,
377
+ ref: this.branch
378
+ });
379
+ return Array.isArray(response.data);
380
+ } catch {
381
+ return false;
382
+ }
383
+ }
384
+ /**
385
+ * Check if path is a file
386
+ */
387
+ async isFile(relativePath) {
388
+ try {
389
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
390
+ const response = await this.octokit.repos.getContent({
391
+ owner: this.owner,
392
+ repo: this.repo,
393
+ path: fullPath,
394
+ ref: this.branch
395
+ });
396
+ return !Array.isArray(response.data) && response.data.type === "file";
397
+ } catch {
398
+ return false;
399
+ }
400
+ }
401
+ /**
402
+ * Check if file or directory exists
403
+ */
404
+ async exists(relativePath) {
405
+ try {
406
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
407
+ await this.octokit.repos.getContent({
408
+ owner: this.owner,
409
+ repo: this.repo,
410
+ path: fullPath,
411
+ ref: this.branch
412
+ });
413
+ return true;
414
+ } catch {
415
+ return false;
416
+ }
417
+ }
418
+ /**
419
+ * Check if directory exists
420
+ */
421
+ async directoryExists(relativePath) {
422
+ return this.isDirectory(relativePath);
423
+ }
424
+ /**
425
+ * Check if file exists
426
+ */
427
+ async fileExists(relativePath) {
428
+ return this.isFile(relativePath);
429
+ }
430
+ /**
431
+ * Get base path
432
+ */
433
+ getBasePath() {
434
+ return this.basePath;
435
+ }
436
+ /**
437
+ * Convert relative path to absolute path
438
+ */
439
+ resolve(relativePath) {
440
+ return this.pathSystem.join(this.basePath, relativePath);
441
+ }
442
+ /**
443
+ * Get file size in bytes
444
+ */
445
+ async getFileSize(relativePath) {
446
+ try {
447
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
448
+ const cached = this.fileCache.get(fullPath);
449
+ if (cached) {
450
+ return cached.size;
451
+ }
452
+ const response = await this.octokit.repos.getContent({
453
+ owner: this.owner,
454
+ repo: this.repo,
455
+ path: fullPath,
456
+ ref: this.branch
457
+ });
458
+ if ("size" in response.data) {
459
+ return response.data.size;
460
+ }
461
+ return new Error(`Cannot get size for ${relativePath}`);
462
+ } catch (error) {
463
+ return error instanceof Error ? error : new Error(`Failed to get file size at ${relativePath}`);
464
+ }
465
+ }
466
+ /**
467
+ * Get file last modified time
468
+ */
469
+ async getFileUpdatedTime(relativePath) {
470
+ try {
471
+ const commits = await this.octokit.repos.listCommits({
472
+ owner: this.owner,
473
+ repo: this.repo,
474
+ path: relativePath,
475
+ sha: this.branch,
476
+ per_page: 1
477
+ });
478
+ if (commits.data.length > 0 && commits.data[0].commit.author?.date) {
479
+ return new Date(commits.data[0].commit.author.date);
480
+ }
481
+ return new Error(`No commits found for ${relativePath}`);
482
+ } catch (error) {
483
+ return error instanceof Error ? error : new Error(`Failed to get file modified time at ${relativePath}`);
484
+ }
485
+ }
486
+ /**
487
+ * Get file creation time (first commit)
488
+ */
489
+ async getFileCreatedTime(relativePath) {
490
+ try {
491
+ const commits = await this.octokit.repos.listCommits({
492
+ owner: this.owner,
493
+ repo: this.repo,
494
+ path: relativePath,
495
+ sha: this.branch
496
+ });
497
+ const lastCommit = commits.data[commits.data.length - 1];
498
+ if (lastCommit?.commit.author?.date) {
499
+ return new Date(lastCommit.commit.author.date);
500
+ }
501
+ return new Error(`No commits found for ${relativePath}`);
502
+ } catch (error) {
503
+ return error instanceof Error ? error : new Error(`Failed to get file created time at ${relativePath}`);
504
+ }
505
+ }
506
+ /**
507
+ * Get cached SHA for a file
508
+ */
509
+ getCachedSha(relativePath) {
510
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
511
+ return this.fileCache.get(fullPath)?.sha;
512
+ }
513
+ };
514
+ var DocFileSystemOctokitWrite = class {
515
+ octokit;
516
+ owner;
517
+ repo;
518
+ basePath;
519
+ branch;
520
+ pathSystem;
521
+ reader;
522
+ constructor(props) {
523
+ this.owner = props.owner;
524
+ this.repo = props.repo;
525
+ this.basePath = props.basePath;
526
+ this.branch = props.branch ?? "main";
527
+ this.pathSystem = props.pathSystem ?? new DocPathSystem();
528
+ this.octokit = new Octokit({ auth: props.token });
529
+ this.reader = props.reader;
530
+ Object.freeze(this);
531
+ }
532
+ /**
533
+ * Write content to file in GitHub repository
534
+ */
535
+ async writeFile(relativePath, content) {
536
+ try {
537
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
538
+ const encodedContent = Buffer.from(content).toString("base64");
539
+ let sha;
540
+ if (this.reader && "getCachedSha" in this.reader) {
541
+ sha = this.reader.getCachedSha(
542
+ relativePath
543
+ );
544
+ }
545
+ if (!sha) {
546
+ try {
547
+ const existing = await this.octokit.repos.getContent({
548
+ owner: this.owner,
549
+ repo: this.repo,
550
+ path: fullPath,
551
+ ref: this.branch
552
+ });
553
+ if ("sha" in existing.data) {
554
+ sha = existing.data.sha;
555
+ }
556
+ } catch {
557
+ }
558
+ }
559
+ await this.octokit.repos.createOrUpdateFileContents({
560
+ owner: this.owner,
561
+ repo: this.repo,
562
+ path: fullPath,
563
+ message: `Update ${fullPath}`,
564
+ content: encodedContent,
565
+ branch: this.branch,
566
+ sha
567
+ });
568
+ return null;
569
+ } catch (error) {
570
+ return error instanceof Error ? error : new Error(`Failed to write file at ${relativePath}`);
571
+ }
572
+ }
573
+ /**
574
+ * Delete file from GitHub repository
575
+ */
576
+ async deleteFile(relativePath) {
577
+ try {
578
+ const fullPath = this.pathSystem.join(this.basePath, relativePath);
579
+ let sha;
580
+ if (this.reader && "getCachedSha" in this.reader) {
581
+ sha = this.reader.getCachedSha(
582
+ relativePath
583
+ );
584
+ }
585
+ if (!sha) {
586
+ const existing = await this.octokit.repos.getContent({
587
+ owner: this.owner,
588
+ repo: this.repo,
589
+ path: fullPath,
590
+ ref: this.branch
591
+ });
592
+ if ("sha" in existing.data) {
593
+ sha = existing.data.sha;
594
+ }
595
+ }
596
+ if (!sha) {
597
+ return new Error(`Cannot delete file at ${relativePath}: SHA not found`);
598
+ }
599
+ await this.octokit.repos.deleteFile({
600
+ owner: this.owner,
601
+ repo: this.repo,
602
+ path: fullPath,
603
+ message: `Delete ${fullPath}`,
604
+ sha,
605
+ branch: this.branch
606
+ });
607
+ return null;
608
+ } catch (error) {
609
+ return error instanceof Error ? error : new Error(`Failed to delete file at ${relativePath}`);
610
+ }
611
+ }
612
+ /**
613
+ * Create directory by creating a .gitkeep file
614
+ */
615
+ async createEmptyDirectory(relativePath) {
616
+ try {
617
+ const gitkeepPath = this.pathSystem.join(relativePath, ".gitkeep");
618
+ return await this.writeFile(gitkeepPath, "");
619
+ } catch (error) {
620
+ return error instanceof Error ? error : new Error(`Failed to create directory at ${relativePath}`);
621
+ }
622
+ }
623
+ /**
624
+ * Create directory if it doesn't exist
625
+ */
626
+ async createDirectory(relativePath) {
627
+ try {
628
+ if (this.reader) {
629
+ const exists = await this.reader.exists(relativePath);
630
+ if (exists) {
631
+ return null;
632
+ }
633
+ }
634
+ return await this.createEmptyDirectory(relativePath);
635
+ } catch (error) {
636
+ return error instanceof Error ? error : new Error(`Failed to ensure directory exists at ${relativePath}`);
637
+ }
638
+ }
639
+ /**
640
+ * Copy file to new location
641
+ */
642
+ async copyFile(sourcePath, destinationPath) {
643
+ try {
644
+ if (!this.reader) {
645
+ return new Error("Reader is required for copy operation");
646
+ }
647
+ const content = await this.reader.readFile(sourcePath);
648
+ if (content instanceof Error) {
649
+ return content;
650
+ }
651
+ if (content === null) {
652
+ return new Error(`Source file ${sourcePath} does not exist`);
653
+ }
654
+ return await this.writeFile(destinationPath, content);
655
+ } catch (error) {
656
+ return error instanceof Error ? error : new Error(
657
+ `Failed to copy file from ${sourcePath} to ${destinationPath}`
658
+ );
659
+ }
660
+ }
661
+ /**
662
+ * Move file to new location
663
+ */
664
+ async moveFile(sourcePath, destinationPath) {
665
+ try {
666
+ const copyResult = await this.copyFile(sourcePath, destinationPath);
667
+ if (copyResult !== null) {
668
+ return copyResult;
669
+ }
670
+ return await this.deleteFile(sourcePath);
671
+ } catch (error) {
672
+ return error instanceof Error ? error : new Error(
673
+ `Failed to move file from ${sourcePath} to ${destinationPath}`
674
+ );
675
+ }
676
+ }
677
+ };
678
+
679
+ // lib/modules/file-system/doc-file-system-octokit.ts
680
+ var DocFileSystemOctokit = class extends DocFileSystem {
681
+ owner;
682
+ repo;
683
+ branch;
684
+ constructor(props) {
685
+ const reader = new DocFileSystemOctokitRead({
686
+ owner: props.owner,
687
+ repo: props.repo,
688
+ basePath: props.basePath,
689
+ branch: props.branch,
690
+ token: props.token,
691
+ pathSystem: props.pathSystem
692
+ });
693
+ const writer = new DocFileSystemOctokitWrite({
694
+ owner: props.owner,
695
+ repo: props.repo,
696
+ basePath: props.basePath,
697
+ branch: props.branch,
698
+ token: props.token,
699
+ pathSystem: props.pathSystem,
700
+ reader
701
+ });
702
+ super({
703
+ basePath: props.basePath,
704
+ pathSystem: props.pathSystem,
705
+ reader,
706
+ writer
707
+ });
708
+ this.owner = props.owner;
709
+ this.repo = props.repo;
710
+ this.branch = props.branch ?? "main";
711
+ Object.freeze(this);
712
+ }
713
+ };
714
+
715
+ export { DocFileSystemOctokit, DocFileSystemOctokitRead, DocFileSystemOctokitWrite };