Skip to content

Commit d2aadaf

Browse files
committed
Add JSON and GitHub file system support to docs-client
Introduces JSON-based and GitHub-based file system implementations, including debug scripts, sample data, and comprehensive tests. Updates package dependencies to include @octokit/rest and related packages, and refactors core file system logic to support new backends.
1 parent 4368a55 commit d2aadaf

27 files changed

+2643
-185
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,29 @@ check-studio:
1515
# Deploy docs package
1616
deploy:
1717
bun biome check . --fix --unsafe
18-
bun run --cwd packages/docs check
1918
bun test
19+
bun run --cwd packages/docs check
2020
bun run --cwd packages/docs deploy
2121

2222
# Deploy client (with checks and tests)
2323
deploy-client:
2424
bun biome check . --fix --unsafe
25-
bun run --cwd packages/docs-client check
2625
bun test
26+
bun run --cwd packages/docs-client check
2727
bun run --cwd packages/docs-client deploy
2828

2929
# Deploy router
3030
deploy-router:
3131
bun biome check . --fix --unsafe
32-
bun run --cwd packages/docs-router check
3332
bun test
33+
bun run --cwd packages/docs-router check
3434
bun run --cwd packages/docs-router deploy
3535

3636
# Deploy studio
3737
deploy-studio:
3838
bun biome check . --fix --unsafe
39-
bun run --cwd packages/docs-studio check
4039
bun test
40+
bun run --cwd packages/docs-studio check
4141
bun run --cwd packages/docs-studio deploy
4242

4343
# Run all checks

bun.lock

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
},
2222
"packages/docs": {
2323
"name": "@interactive-inc/docs",
24-
"version": "1.0.10",
24+
"version": "1.0.13",
2525
"bin": {
2626
"docs": "bin/docs.js",
2727
},
@@ -35,8 +35,9 @@
3535
},
3636
"packages/docs-client": {
3737
"name": "@interactive-inc/docs-client",
38-
"version": "1.0.17",
38+
"version": "1.0.19",
3939
"dependencies": {
40+
"@octokit/rest": "^22.0.0",
4041
"yaml": "^2.0.0",
4142
},
4243
"peerDependencies": {
@@ -45,7 +46,7 @@
4546
},
4647
"packages/docs-router": {
4748
"name": "@interactive-inc/docs-router",
48-
"version": "1.0.7",
49+
"version": "1.0.8",
4950
"dependencies": {
5051
"@hono/zod-validator": "^0.7.0",
5152
"@interactive-inc/docs-client": "latest",
@@ -58,7 +59,7 @@
5859
},
5960
"packages/docs-studio": {
6061
"name": "@interactive-inc/docs-studio",
61-
"version": "1.0.4",
62+
"version": "1.0.7",
6263
"dependencies": {
6364
"@hono/node-server": "^1.0.0",
6465
},
@@ -322,6 +323,30 @@
322323

323324
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/[email protected]", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-qFfbWFA7r1Sd8D697L7GkTd36yqDuTkvz0KfOGkgXR8EUhQn3/EDNIR/qUdQNMT8IjmasBvHWuXeisxtXTQT2g=="],
324325

326+
"@octokit/auth-token": ["@octokit/[email protected]", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
327+
328+
"@octokit/core": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.1", "@octokit/request": "^10.0.2", "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ=="],
329+
330+
"@octokit/endpoint": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ=="],
331+
332+
"@octokit/graphql": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/request": "^10.0.2", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg=="],
333+
334+
"@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
335+
336+
"@octokit/plugin-paginate-rest": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/types": "^14.1.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw=="],
337+
338+
"@octokit/plugin-request-log": ["@octokit/[email protected]", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="],
339+
340+
"@octokit/plugin-rest-endpoint-methods": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/types": "^14.1.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g=="],
341+
342+
"@octokit/request": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/endpoint": "^11.0.0", "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA=="],
343+
344+
"@octokit/request-error": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/types": "^14.0.0" } }, "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg=="],
345+
346+
"@octokit/rest": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="],
347+
348+
"@octokit/types": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
349+
325350
"@pkgjs/parseargs": ["@pkgjs/[email protected]", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
326351

327352
"@radix-ui/number": ["@radix-ui/[email protected]", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
@@ -668,6 +693,8 @@
668693

669694
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
670695

696+
"before-after-hook": ["[email protected]", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
697+
671698
"binary-extensions": ["[email protected]", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
672699

673700
"birpc": ["[email protected]", "", {}, "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ=="],
@@ -804,6 +831,8 @@
804831

805832
"express-rate-limit": ["[email protected]", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
806833

834+
"fast-content-type-parse": ["[email protected]", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="],
835+
807836
"fast-deep-equal": ["[email protected]", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
808837

809838
"fast-json-stable-stringify": ["[email protected]", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
@@ -1218,6 +1247,8 @@
12181247

12191248
"unist-util-visit-parents": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="],
12201249

1250+
"universal-user-agent": ["[email protected]", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="],
1251+
12211252
"unpipe": ["[email protected]", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
12221253

12231254
"unplugin": ["[email protected]", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw=="],
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { readFile } from "node:fs/promises"
2+
import { join } from "node:path"
3+
import { DocClient } from "../lib/doc-client"
4+
import { DocFileSystem } from "../lib/doc-file-system"
5+
import { DocFileSystemJsonRead } from "../lib/doc-file-system-json-read"
6+
import { DocFileSystemWrite } from "../lib/doc-file-system-write"
7+
8+
/**
9+
* Debug script for JSON file system
10+
*/
11+
12+
console.log("JSON File System Debug")
13+
console.log("======================\n")
14+
15+
// Load JSON data
16+
const jsonPath = join(import.meta.dir, "sample-docs-data.json")
17+
console.log("Loading:", jsonPath)
18+
19+
let jsonData: unknown
20+
try {
21+
const jsonContent = await readFile(jsonPath, "utf-8")
22+
jsonData = JSON.parse(jsonContent)
23+
console.log("✅ Loaded\n")
24+
} catch (error) {
25+
console.error("❌ Failed:", error)
26+
process.exit(1)
27+
}
28+
29+
// Setup JSON reader
30+
const jsonReader = new DocFileSystemJsonRead({
31+
data: jsonData,
32+
basePath: "json://docs-sample",
33+
})
34+
35+
// Setup local writer (not used)
36+
const localWriter = new DocFileSystemWrite({
37+
basePath: "/tmp/docs-json-output",
38+
})
39+
40+
// Create file system
41+
const fileSystem = new DocFileSystem({
42+
basePath: "json://docs-sample",
43+
reader: jsonReader,
44+
writer: localWriter,
45+
})
46+
47+
const client = new DocClient({
48+
fileSystem,
49+
})
50+
51+
console.log("Configuration:")
52+
console.log("- Reader: JSON (sample-docs-data.json)")
53+
console.log("- Writer: Local (/tmp/docs-json-output)")
54+
console.log("- Base path:", client.basePath())
55+
56+
// JSON data overview
57+
console.log("\n--- Data Overview ---")
58+
const allPaths = jsonReader.getAllFilePaths()
59+
console.log(`Total files: ${allPaths.length}`)
60+
console.log("\nFiles:")
61+
allPaths.forEach((path) => {
62+
const ext = path.split(".").pop()
63+
const icon =
64+
ext === "md" ? "📝" : ext === "json" ? "📦" : ext === "ts" ? "📘" : "📄"
65+
console.log(` ${icon} ${path}`)
66+
})
67+
68+
// Root directory
69+
console.log("\n--- Root Directory ---")
70+
const rootDir = client.directory("")
71+
const rootDirs = await rootDir.directoryNames()
72+
const rootFiles = await rootDir.fileNames()
73+
74+
if (!(rootDirs instanceof Error)) {
75+
console.log("Directories:", rootDirs.join(", "))
76+
}
77+
78+
if (!(rootFiles instanceof Error)) {
79+
console.log("Files:", rootFiles.join(", "))
80+
}
81+
82+
// Docs directory
83+
console.log("\n--- docs/ ---")
84+
const docsDir = client.directory("docs")
85+
const docsDirs = await docsDir.directoryNames()
86+
const docsFiles = await docsDir.fileNames()
87+
88+
if (!(docsDirs instanceof Error)) {
89+
console.log("Subdirectories:", docsDirs.join(", "))
90+
}
91+
92+
if (!(docsFiles instanceof Error)) {
93+
console.log("Files:", docsFiles.join(", "))
94+
}
95+
96+
// Read README.md
97+
console.log("\n--- README.md ---")
98+
const readmeContent = await jsonReader.readFile("README.md")
99+
if (typeof readmeContent === "string") {
100+
console.log(`First 200 chars: ${readmeContent.slice(0, 200)}...`)
101+
const size = await jsonReader.getFileSize("README.md")
102+
console.log(`Size: ${size} bytes`)
103+
}
104+
105+
// Read and parse docs/index.md
106+
console.log("\n--- docs/index.md ---")
107+
const indexFile = await client.file("docs/index.md").read()
108+
if (!(indexFile instanceof Error)) {
109+
console.log("Title:", indexFile.content.title)
110+
console.log("Description:", indexFile.content.description)
111+
const meta = indexFile.content.meta()
112+
console.log("Meta keys:", Object.keys(meta))
113+
}
114+
115+
// Read docs/getting-started/installation.md
116+
console.log("\n--- docs/getting-started/installation.md ---")
117+
const installFile = await client
118+
.file("docs/getting-started/installation.md")
119+
.read()
120+
if (!(installFile instanceof Error)) {
121+
console.log("Title:", installFile.content.title)
122+
const meta = installFile.content.meta()
123+
console.log("Meta keys:", Object.keys(meta))
124+
}
125+
126+
// Path validation
127+
console.log("\n--- Path Validation ---")
128+
129+
console.log("File existence:")
130+
const filesToCheck = ["README.md", "docs/index.md", "non-existent.md"]
131+
for (const file of filesToCheck) {
132+
const exists = await jsonReader.fileExists(file)
133+
console.log(` ${file}: ${exists ? "✓" : "✗"}`)
134+
}
135+
136+
console.log("\nDirectory existence:")
137+
const dirsToCheck = ["docs", "docs/getting-started", "non-existent"]
138+
for (const dir of dirsToCheck) {
139+
const exists = await jsonReader.directoryExists(dir)
140+
console.log(` ${dir}/: ${exists ? "✓" : "✗"}`)
141+
}
142+
143+
// Nested directories
144+
console.log("\n--- docs/getting-started/ ---")
145+
const gettingStartedDir = client.directory("docs/getting-started")
146+
const gsFiles = await gettingStartedDir.fileNames()
147+
148+
if (!(gsFiles instanceof Error)) {
149+
console.log("Files:", gsFiles.join(", "))
150+
}
151+
152+
console.log("\n--- docs/guides/ ---")
153+
const guidesDir = client.directory("docs/guides")
154+
const guidesFiles = await guidesDir.fileNames()
155+
156+
if (!(guidesFiles instanceof Error)) {
157+
console.log("Files:", guidesFiles.join(", "))
158+
}
159+
160+
// Error cases
161+
console.log("\n--- Error Cases ---")
162+
163+
const nonExistent = await jsonReader.readFile("non-existent.md")
164+
console.log("Non-existent file:", nonExistent === null ? "null ✓" : "error")
165+
166+
const modTime = await jsonReader.getFileModifiedTime("README.md")
167+
console.log(
168+
"Modified time:",
169+
modTime instanceof Error
170+
? `Error: ${modTime.message.slice(0, 50)}...`
171+
: "unexpected",
172+
)
173+
174+
console.log("\n✅ Complete")

0 commit comments

Comments
 (0)