@xano/developer-mcp 1.0.20 → 1.0.22
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/README.md +100 -19
- package/dist/index.js +4 -227
- package/dist/meta_api_docs/format.d.ts +16 -1
- package/dist/meta_api_docs/format.js +24 -6
- package/dist/meta_api_docs/format.test.d.ts +1 -0
- package/dist/meta_api_docs/format.test.js +274 -0
- package/dist/meta_api_docs/index.test.d.ts +1 -0
- package/dist/meta_api_docs/index.test.js +128 -0
- package/dist/meta_api_docs/types.test.d.ts +1 -0
- package/dist/meta_api_docs/types.test.js +132 -0
- package/dist/run_api_docs/format.d.ts +1 -0
- package/dist/run_api_docs/format.js +3 -170
- package/dist/run_api_docs/format.test.d.ts +1 -0
- package/dist/run_api_docs/format.test.js +86 -0
- package/dist/run_api_docs/index.test.d.ts +1 -0
- package/dist/run_api_docs/index.test.js +127 -0
- package/dist/templates/init-workspace.js +4 -4
- package/dist/templates/xanoscript-index.d.ts +3 -1
- package/dist/templates/xanoscript-index.js +54 -51
- package/dist/xanoscript.d.ts +41 -0
- package/dist/xanoscript.js +261 -0
- package/dist/xanoscript.test.d.ts +1 -0
- package/dist/xanoscript.test.js +303 -0
- package/dist/xanoscript_docs/README.md +53 -37
- package/dist/xanoscript_docs/agents.md +1 -1
- package/dist/xanoscript_docs/apis.md +6 -3
- package/dist/xanoscript_docs/branch.md +239 -0
- package/dist/xanoscript_docs/functions.md +6 -6
- package/dist/xanoscript_docs/integrations.md +43 -1
- package/dist/xanoscript_docs/middleware.md +321 -0
- package/dist/xanoscript_docs/performance.md +1 -1
- package/dist/xanoscript_docs/realtime.md +113 -1
- package/dist/xanoscript_docs/tasks.md +2 -2
- package/dist/xanoscript_docs/tools.md +3 -3
- package/dist/xanoscript_docs/types.md +25 -8
- package/dist/xanoscript_docs/workspace.md +209 -0
- package/dist/xanoscript_docs_auto/README.md +119 -0
- package/dist/xanoscript_docs_auto/agents.md +446 -0
- package/dist/xanoscript_docs_auto/apis.md +517 -0
- package/dist/xanoscript_docs_auto/control-flow.md +543 -0
- package/dist/xanoscript_docs_auto/database.md +551 -0
- package/dist/xanoscript_docs_auto/debugging.md +527 -0
- package/dist/xanoscript_docs_auto/filters.md +464 -0
- package/dist/xanoscript_docs_auto/functions.md +431 -0
- package/dist/xanoscript_docs_auto/integrations.md +657 -0
- package/dist/xanoscript_docs_auto/mcp-servers.md +408 -0
- package/dist/xanoscript_docs_auto/operators.md +368 -0
- package/dist/xanoscript_docs_auto/syntax.md +287 -0
- package/dist/xanoscript_docs_auto/tables.md +447 -0
- package/dist/xanoscript_docs_auto/tasks.md +479 -0
- package/dist/xanoscript_docs_auto/testing.md +574 -0
- package/dist/xanoscript_docs_auto/tools.md +485 -0
- package/dist/xanoscript_docs_auto/triggers.md +595 -0
- package/dist/xanoscript_docs_auto/types.md +323 -0
- package/dist/xanoscript_docs_auto/variables.md +462 -0
- package/dist/xanoscript_docs_auto/version.json +5 -0
- package/package.json +6 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XanoScript Documentation Module
|
|
3
|
+
*
|
|
4
|
+
* This module contains the core logic for XanoScript documentation
|
|
5
|
+
* handling, separated from the MCP server for testability.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { minimatch } from "minimatch";
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Documentation Configuration
|
|
12
|
+
// =============================================================================
|
|
13
|
+
export const XANOSCRIPT_DOCS_V2 = {
|
|
14
|
+
readme: {
|
|
15
|
+
file: "README.md",
|
|
16
|
+
applyTo: [],
|
|
17
|
+
description: "XanoScript overview, workspace structure, and quick reference",
|
|
18
|
+
},
|
|
19
|
+
syntax: {
|
|
20
|
+
file: "syntax.md",
|
|
21
|
+
applyTo: ["**/*.xs"],
|
|
22
|
+
description: "Expressions, operators, and filters for all XanoScript code",
|
|
23
|
+
},
|
|
24
|
+
types: {
|
|
25
|
+
file: "types.md",
|
|
26
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tools/**/*.xs", "agents/**/*.xs"],
|
|
27
|
+
description: "Data types, input blocks, and validation",
|
|
28
|
+
},
|
|
29
|
+
tables: {
|
|
30
|
+
file: "tables.md",
|
|
31
|
+
applyTo: ["tables/*.xs"],
|
|
32
|
+
description: "Database schema definitions with indexes and relationships",
|
|
33
|
+
},
|
|
34
|
+
functions: {
|
|
35
|
+
file: "functions.md",
|
|
36
|
+
applyTo: ["functions/**/*.xs"],
|
|
37
|
+
description: "Reusable function stacks with inputs and responses",
|
|
38
|
+
},
|
|
39
|
+
apis: {
|
|
40
|
+
file: "apis.md",
|
|
41
|
+
applyTo: ["apis/**/*.xs"],
|
|
42
|
+
description: "HTTP endpoint definitions with authentication and CRUD patterns",
|
|
43
|
+
},
|
|
44
|
+
tasks: {
|
|
45
|
+
file: "tasks.md",
|
|
46
|
+
applyTo: ["tasks/*.xs"],
|
|
47
|
+
description: "Scheduled and cron jobs",
|
|
48
|
+
},
|
|
49
|
+
triggers: {
|
|
50
|
+
file: "triggers.md",
|
|
51
|
+
applyTo: ["triggers/**/*.xs"],
|
|
52
|
+
description: "Event-driven handlers (table, realtime, workspace, agent, MCP)",
|
|
53
|
+
},
|
|
54
|
+
database: {
|
|
55
|
+
file: "database.md",
|
|
56
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs", "tools/**/*.xs"],
|
|
57
|
+
description: "All db.* operations: query, get, add, edit, patch, delete",
|
|
58
|
+
},
|
|
59
|
+
agents: {
|
|
60
|
+
file: "agents.md",
|
|
61
|
+
applyTo: ["agents/**/*.xs"],
|
|
62
|
+
description: "AI agent configuration with LLM providers and tools",
|
|
63
|
+
},
|
|
64
|
+
tools: {
|
|
65
|
+
file: "tools.md",
|
|
66
|
+
applyTo: ["tools/**/*.xs"],
|
|
67
|
+
description: "AI tools for agents and MCP servers",
|
|
68
|
+
},
|
|
69
|
+
"mcp-servers": {
|
|
70
|
+
file: "mcp-servers.md",
|
|
71
|
+
applyTo: ["mcp_servers/**/*.xs"],
|
|
72
|
+
description: "MCP server definitions exposing tools",
|
|
73
|
+
},
|
|
74
|
+
testing: {
|
|
75
|
+
file: "testing.md",
|
|
76
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
77
|
+
description: "Unit tests, mocks, and assertions",
|
|
78
|
+
},
|
|
79
|
+
integrations: {
|
|
80
|
+
file: "integrations.md",
|
|
81
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs"],
|
|
82
|
+
description: "Cloud storage, Redis, security, and external APIs",
|
|
83
|
+
},
|
|
84
|
+
frontend: {
|
|
85
|
+
file: "frontend.md",
|
|
86
|
+
applyTo: ["static/**/*"],
|
|
87
|
+
description: "Static frontend development and deployment",
|
|
88
|
+
},
|
|
89
|
+
run: {
|
|
90
|
+
file: "run.md",
|
|
91
|
+
applyTo: ["run/**/*.xs"],
|
|
92
|
+
description: "Run job and service configurations for the Xano Job Runner",
|
|
93
|
+
},
|
|
94
|
+
addons: {
|
|
95
|
+
file: "addons.md",
|
|
96
|
+
applyTo: ["addons/*.xs", "functions/**/*.xs", "apis/**/*.xs"],
|
|
97
|
+
description: "Reusable subqueries for fetching related data",
|
|
98
|
+
},
|
|
99
|
+
debugging: {
|
|
100
|
+
file: "debugging.md",
|
|
101
|
+
applyTo: ["**/*.xs"],
|
|
102
|
+
description: "Logging, inspecting, and debugging XanoScript execution",
|
|
103
|
+
},
|
|
104
|
+
performance: {
|
|
105
|
+
file: "performance.md",
|
|
106
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
107
|
+
description: "Performance optimization best practices",
|
|
108
|
+
},
|
|
109
|
+
realtime: {
|
|
110
|
+
file: "realtime.md",
|
|
111
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "triggers/**/*.xs"],
|
|
112
|
+
description: "Real-time channels and events for push updates",
|
|
113
|
+
},
|
|
114
|
+
schema: {
|
|
115
|
+
file: "schema.md",
|
|
116
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
117
|
+
description: "Runtime schema parsing and validation",
|
|
118
|
+
},
|
|
119
|
+
security: {
|
|
120
|
+
file: "security.md",
|
|
121
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
122
|
+
description: "Security best practices for authentication and authorization",
|
|
123
|
+
},
|
|
124
|
+
streaming: {
|
|
125
|
+
file: "streaming.md",
|
|
126
|
+
applyTo: ["functions/**/*.xs", "apis/**/*.xs"],
|
|
127
|
+
description: "Streaming data from files, requests, and responses",
|
|
128
|
+
},
|
|
129
|
+
middleware: {
|
|
130
|
+
file: "middleware.md",
|
|
131
|
+
applyTo: ["middleware/**/*.xs"],
|
|
132
|
+
description: "Request/response interceptors for functions, queries, tasks, and tools",
|
|
133
|
+
},
|
|
134
|
+
branch: {
|
|
135
|
+
file: "branch.md",
|
|
136
|
+
applyTo: ["branch.xs"],
|
|
137
|
+
description: "Branch-level settings: middleware, history retention, visual styling",
|
|
138
|
+
},
|
|
139
|
+
workspace: {
|
|
140
|
+
file: "workspace.md",
|
|
141
|
+
applyTo: ["workspace.xs"],
|
|
142
|
+
description: "Workspace-level settings: environment variables, preferences, realtime",
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
// =============================================================================
|
|
146
|
+
// Core Functions
|
|
147
|
+
// =============================================================================
|
|
148
|
+
/**
|
|
149
|
+
* Get list of topics that apply to a given file path based on applyTo patterns
|
|
150
|
+
*/
|
|
151
|
+
export function getDocsForFilePath(filePath) {
|
|
152
|
+
const matches = [];
|
|
153
|
+
for (const [topic, config] of Object.entries(XANOSCRIPT_DOCS_V2)) {
|
|
154
|
+
if (topic === "readme")
|
|
155
|
+
continue; // Don't auto-include readme
|
|
156
|
+
for (const pattern of config.applyTo) {
|
|
157
|
+
if (minimatch(filePath, pattern)) {
|
|
158
|
+
matches.push(topic);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Always include syntax as foundation (if not already matched)
|
|
164
|
+
if (!matches.includes("syntax")) {
|
|
165
|
+
matches.unshift("syntax");
|
|
166
|
+
}
|
|
167
|
+
return matches;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Extract just the Quick Reference section from a doc
|
|
171
|
+
*/
|
|
172
|
+
export function extractQuickReference(content, topic) {
|
|
173
|
+
const lines = content.split("\n");
|
|
174
|
+
const startIdx = lines.findIndex((l) => l.startsWith("## Quick Reference"));
|
|
175
|
+
if (startIdx === -1) {
|
|
176
|
+
// Fallback: return first 50 lines or up to first ## section
|
|
177
|
+
const firstSection = lines.findIndex((l, i) => i > 0 && l.startsWith("## "));
|
|
178
|
+
return lines.slice(0, firstSection > 0 ? firstSection : 50).join("\n");
|
|
179
|
+
}
|
|
180
|
+
// Find the next ## section after Quick Reference
|
|
181
|
+
let endIdx = lines.findIndex((l, i) => i > startIdx && l.startsWith("## "));
|
|
182
|
+
if (endIdx === -1)
|
|
183
|
+
endIdx = lines.length;
|
|
184
|
+
// Include topic header for context
|
|
185
|
+
const header = `# ${topic}\n\n`;
|
|
186
|
+
return header + lines.slice(startIdx, endIdx).join("\n");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get the documentation version from the version.json file
|
|
190
|
+
*/
|
|
191
|
+
export function getXanoscriptDocsVersion(docsPath) {
|
|
192
|
+
try {
|
|
193
|
+
const versionFile = readFileSync(join(docsPath, "version.json"), "utf-8");
|
|
194
|
+
return JSON.parse(versionFile).version || "unknown";
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return "unknown";
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Read XanoScript documentation with v2 structure
|
|
202
|
+
*/
|
|
203
|
+
export function readXanoscriptDocsV2(docsPath, args) {
|
|
204
|
+
const mode = args?.mode || "full";
|
|
205
|
+
const version = getXanoscriptDocsVersion(docsPath);
|
|
206
|
+
try {
|
|
207
|
+
// Default: return README
|
|
208
|
+
if (!args?.topic && !args?.file_path) {
|
|
209
|
+
const readme = readFileSync(join(docsPath, "README.md"), "utf-8");
|
|
210
|
+
return `${readme}\n\n---\nDocumentation version: ${version}`;
|
|
211
|
+
}
|
|
212
|
+
// Context-aware: return docs matching file pattern
|
|
213
|
+
if (args?.file_path) {
|
|
214
|
+
const topics = getDocsForFilePath(args.file_path);
|
|
215
|
+
if (topics.length === 0) {
|
|
216
|
+
return `No documentation found for file pattern: ${args.file_path}\n\nAvailable topics: ${Object.keys(XANOSCRIPT_DOCS_V2).join(", ")}`;
|
|
217
|
+
}
|
|
218
|
+
const docs = topics.map((t) => {
|
|
219
|
+
const config = XANOSCRIPT_DOCS_V2[t];
|
|
220
|
+
const content = readFileSync(join(docsPath, config.file), "utf-8");
|
|
221
|
+
return mode === "quick_reference"
|
|
222
|
+
? extractQuickReference(content, t)
|
|
223
|
+
: content;
|
|
224
|
+
});
|
|
225
|
+
const header = `# XanoScript Documentation for: ${args.file_path}\n\nMatched topics: ${topics.join(", ")}\nMode: ${mode}\nVersion: ${version}\n\n---\n\n`;
|
|
226
|
+
return header + docs.join("\n\n---\n\n");
|
|
227
|
+
}
|
|
228
|
+
// Topic-based: return specific doc
|
|
229
|
+
if (args?.topic) {
|
|
230
|
+
const config = XANOSCRIPT_DOCS_V2[args.topic];
|
|
231
|
+
if (!config) {
|
|
232
|
+
const availableTopics = Object.keys(XANOSCRIPT_DOCS_V2).join(", ");
|
|
233
|
+
return `Error: Unknown topic "${args.topic}".\n\nAvailable topics: ${availableTopics}`;
|
|
234
|
+
}
|
|
235
|
+
const content = readFileSync(join(docsPath, config.file), "utf-8");
|
|
236
|
+
const doc = mode === "quick_reference"
|
|
237
|
+
? extractQuickReference(content, args.topic)
|
|
238
|
+
: content;
|
|
239
|
+
return `${doc}\n\n---\nDocumentation version: ${version}`;
|
|
240
|
+
}
|
|
241
|
+
return "Error: Invalid parameters";
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
245
|
+
return `Error reading XanoScript documentation: ${errorMessage}`;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get available topic names
|
|
250
|
+
*/
|
|
251
|
+
export function getTopicNames() {
|
|
252
|
+
return Object.keys(XANOSCRIPT_DOCS_V2);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get topic descriptions for documentation
|
|
256
|
+
*/
|
|
257
|
+
export function getTopicDescriptions() {
|
|
258
|
+
return Object.entries(XANOSCRIPT_DOCS_V2)
|
|
259
|
+
.map(([k, v]) => `${k} (${v.description.split(".")[0]})`)
|
|
260
|
+
.join(", ");
|
|
261
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { XANOSCRIPT_DOCS_V2, getDocsForFilePath, extractQuickReference, getXanoscriptDocsVersion, readXanoscriptDocsV2, getTopicNames, getTopicDescriptions, } from "./xanoscript.js";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
const DOCS_PATH = join(__dirname, "xanoscript_docs");
|
|
8
|
+
describe("xanoscript module", () => {
|
|
9
|
+
describe("XANOSCRIPT_DOCS_V2", () => {
|
|
10
|
+
it("should have all expected topics", () => {
|
|
11
|
+
const expectedTopics = [
|
|
12
|
+
"readme",
|
|
13
|
+
"syntax",
|
|
14
|
+
"types",
|
|
15
|
+
"tables",
|
|
16
|
+
"functions",
|
|
17
|
+
"apis",
|
|
18
|
+
"tasks",
|
|
19
|
+
"triggers",
|
|
20
|
+
"database",
|
|
21
|
+
"agents",
|
|
22
|
+
"tools",
|
|
23
|
+
"mcp-servers",
|
|
24
|
+
"testing",
|
|
25
|
+
"integrations",
|
|
26
|
+
"frontend",
|
|
27
|
+
"run",
|
|
28
|
+
"addons",
|
|
29
|
+
"debugging",
|
|
30
|
+
"performance",
|
|
31
|
+
"realtime",
|
|
32
|
+
"schema",
|
|
33
|
+
"security",
|
|
34
|
+
"streaming",
|
|
35
|
+
"middleware",
|
|
36
|
+
"branch",
|
|
37
|
+
"workspace",
|
|
38
|
+
];
|
|
39
|
+
expect(Object.keys(XANOSCRIPT_DOCS_V2)).toEqual(expectedTopics);
|
|
40
|
+
});
|
|
41
|
+
it("should have valid DocConfig structure for each topic", () => {
|
|
42
|
+
for (const [key, config] of Object.entries(XANOSCRIPT_DOCS_V2)) {
|
|
43
|
+
expect(config).toHaveProperty("file");
|
|
44
|
+
expect(config).toHaveProperty("applyTo");
|
|
45
|
+
expect(config).toHaveProperty("description");
|
|
46
|
+
expect(typeof config.file).toBe("string");
|
|
47
|
+
expect(Array.isArray(config.applyTo)).toBe(true);
|
|
48
|
+
expect(typeof config.description).toBe("string");
|
|
49
|
+
expect(config.file).toMatch(/\.md$/);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("getDocsForFilePath", () => {
|
|
54
|
+
it("should always include syntax for .xs files", () => {
|
|
55
|
+
const result = getDocsForFilePath("test.xs");
|
|
56
|
+
expect(result).toContain("syntax");
|
|
57
|
+
});
|
|
58
|
+
it("should match apis files", () => {
|
|
59
|
+
const result = getDocsForFilePath("apis/users/create.xs");
|
|
60
|
+
expect(result).toContain("syntax");
|
|
61
|
+
expect(result).toContain("apis");
|
|
62
|
+
expect(result).toContain("types");
|
|
63
|
+
expect(result).toContain("database");
|
|
64
|
+
expect(result).toContain("testing");
|
|
65
|
+
expect(result).toContain("addons");
|
|
66
|
+
});
|
|
67
|
+
it("should match functions files", () => {
|
|
68
|
+
const result = getDocsForFilePath("functions/utils/format.xs");
|
|
69
|
+
expect(result).toContain("syntax");
|
|
70
|
+
expect(result).toContain("functions");
|
|
71
|
+
expect(result).toContain("types");
|
|
72
|
+
expect(result).toContain("database");
|
|
73
|
+
});
|
|
74
|
+
it("should match tables files", () => {
|
|
75
|
+
const result = getDocsForFilePath("tables/users.xs");
|
|
76
|
+
expect(result).toContain("syntax");
|
|
77
|
+
expect(result).toContain("tables");
|
|
78
|
+
});
|
|
79
|
+
it("should match tasks files", () => {
|
|
80
|
+
const result = getDocsForFilePath("tasks/cleanup.xs");
|
|
81
|
+
expect(result).toContain("syntax");
|
|
82
|
+
expect(result).toContain("tasks");
|
|
83
|
+
expect(result).toContain("database");
|
|
84
|
+
expect(result).toContain("integrations");
|
|
85
|
+
});
|
|
86
|
+
it("should match triggers files", () => {
|
|
87
|
+
const result = getDocsForFilePath("triggers/table/users.xs");
|
|
88
|
+
expect(result).toContain("syntax");
|
|
89
|
+
expect(result).toContain("triggers");
|
|
90
|
+
expect(result).toContain("realtime");
|
|
91
|
+
});
|
|
92
|
+
it("should match agents files", () => {
|
|
93
|
+
const result = getDocsForFilePath("agents/assistant/main.xs");
|
|
94
|
+
expect(result).toContain("syntax");
|
|
95
|
+
expect(result).toContain("agents");
|
|
96
|
+
expect(result).toContain("types");
|
|
97
|
+
});
|
|
98
|
+
it("should match tools files", () => {
|
|
99
|
+
const result = getDocsForFilePath("tools/search/main.xs");
|
|
100
|
+
expect(result).toContain("syntax");
|
|
101
|
+
expect(result).toContain("tools");
|
|
102
|
+
expect(result).toContain("types");
|
|
103
|
+
expect(result).toContain("database");
|
|
104
|
+
});
|
|
105
|
+
it("should match mcp_servers files", () => {
|
|
106
|
+
const result = getDocsForFilePath("mcp_servers/myserver/main.xs");
|
|
107
|
+
expect(result).toContain("syntax");
|
|
108
|
+
expect(result).toContain("mcp-servers");
|
|
109
|
+
});
|
|
110
|
+
it("should match middleware files", () => {
|
|
111
|
+
const result = getDocsForFilePath("middleware/auth/main.xs");
|
|
112
|
+
expect(result).toContain("syntax");
|
|
113
|
+
expect(result).toContain("middleware");
|
|
114
|
+
});
|
|
115
|
+
it("should match branch.xs", () => {
|
|
116
|
+
const result = getDocsForFilePath("branch.xs");
|
|
117
|
+
expect(result).toContain("syntax");
|
|
118
|
+
expect(result).toContain("branch");
|
|
119
|
+
});
|
|
120
|
+
it("should match workspace.xs", () => {
|
|
121
|
+
const result = getDocsForFilePath("workspace.xs");
|
|
122
|
+
expect(result).toContain("syntax");
|
|
123
|
+
expect(result).toContain("workspace");
|
|
124
|
+
});
|
|
125
|
+
it("should match static frontend files", () => {
|
|
126
|
+
const result = getDocsForFilePath("static/index.html");
|
|
127
|
+
expect(result).toContain("frontend");
|
|
128
|
+
});
|
|
129
|
+
it("should match run files", () => {
|
|
130
|
+
const result = getDocsForFilePath("run/job/main.xs");
|
|
131
|
+
expect(result).toContain("syntax");
|
|
132
|
+
expect(result).toContain("run");
|
|
133
|
+
});
|
|
134
|
+
it("should not include readme automatically", () => {
|
|
135
|
+
const result = getDocsForFilePath("apis/test.xs");
|
|
136
|
+
expect(result).not.toContain("readme");
|
|
137
|
+
});
|
|
138
|
+
it("should put syntax first if not already matched", () => {
|
|
139
|
+
const result = getDocsForFilePath("some/random/file.xs");
|
|
140
|
+
expect(result[0]).toBe("syntax");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe("extractQuickReference", () => {
|
|
144
|
+
it("should extract Quick Reference section when present", () => {
|
|
145
|
+
const content = `# Title
|
|
146
|
+
|
|
147
|
+
Some intro text.
|
|
148
|
+
|
|
149
|
+
## Quick Reference
|
|
150
|
+
|
|
151
|
+
Quick reference content here.
|
|
152
|
+
|
|
153
|
+
More quick reference.
|
|
154
|
+
|
|
155
|
+
## Next Section
|
|
156
|
+
|
|
157
|
+
Other content.
|
|
158
|
+
`;
|
|
159
|
+
const result = extractQuickReference(content, "test");
|
|
160
|
+
expect(result).toContain("# test");
|
|
161
|
+
expect(result).toContain("## Quick Reference");
|
|
162
|
+
expect(result).toContain("Quick reference content here.");
|
|
163
|
+
expect(result).not.toContain("## Next Section");
|
|
164
|
+
expect(result).not.toContain("Other content.");
|
|
165
|
+
});
|
|
166
|
+
it("should return fallback when no Quick Reference section", () => {
|
|
167
|
+
const content = `# Title
|
|
168
|
+
|
|
169
|
+
Some intro text.
|
|
170
|
+
|
|
171
|
+
## First Section
|
|
172
|
+
|
|
173
|
+
Section content.
|
|
174
|
+
|
|
175
|
+
## Second Section
|
|
176
|
+
|
|
177
|
+
More content.
|
|
178
|
+
`;
|
|
179
|
+
const result = extractQuickReference(content, "test");
|
|
180
|
+
expect(result).toContain("# Title");
|
|
181
|
+
expect(result).toContain("Some intro text.");
|
|
182
|
+
expect(result).not.toContain("## First Section");
|
|
183
|
+
});
|
|
184
|
+
it("should return up to 50 lines if no sections found", () => {
|
|
185
|
+
const lines = Array(100).fill("Line content").join("\n");
|
|
186
|
+
const result = extractQuickReference(lines, "test");
|
|
187
|
+
const resultLines = result.split("\n");
|
|
188
|
+
expect(resultLines.length).toBeLessThanOrEqual(50);
|
|
189
|
+
});
|
|
190
|
+
it("should include Quick Reference to end if no following section", () => {
|
|
191
|
+
const content = `# Title
|
|
192
|
+
|
|
193
|
+
## Quick Reference
|
|
194
|
+
|
|
195
|
+
Quick reference content.
|
|
196
|
+
More content.
|
|
197
|
+
Even more content.
|
|
198
|
+
`;
|
|
199
|
+
const result = extractQuickReference(content, "test");
|
|
200
|
+
expect(result).toContain("Quick reference content.");
|
|
201
|
+
expect(result).toContain("More content.");
|
|
202
|
+
expect(result).toContain("Even more content.");
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe("getXanoscriptDocsVersion", () => {
|
|
206
|
+
it("should return version from valid docs path", () => {
|
|
207
|
+
const version = getXanoscriptDocsVersion(DOCS_PATH);
|
|
208
|
+
expect(typeof version).toBe("string");
|
|
209
|
+
// Version should be a semver-like string or "unknown"
|
|
210
|
+
expect(version).toMatch(/^(\d+\.\d+\.\d+|unknown)$/);
|
|
211
|
+
});
|
|
212
|
+
it("should return unknown for invalid path", () => {
|
|
213
|
+
const version = getXanoscriptDocsVersion("/nonexistent/path");
|
|
214
|
+
expect(version).toBe("unknown");
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe("readXanoscriptDocsV2", () => {
|
|
218
|
+
it("should return README when no args provided", () => {
|
|
219
|
+
const result = readXanoscriptDocsV2(DOCS_PATH);
|
|
220
|
+
expect(result).toContain("Documentation version:");
|
|
221
|
+
});
|
|
222
|
+
it("should return README when empty args provided", () => {
|
|
223
|
+
const result = readXanoscriptDocsV2(DOCS_PATH, {});
|
|
224
|
+
expect(result).toContain("Documentation version:");
|
|
225
|
+
});
|
|
226
|
+
it("should return specific topic documentation", () => {
|
|
227
|
+
const result = readXanoscriptDocsV2(DOCS_PATH, { topic: "syntax" });
|
|
228
|
+
expect(result).toContain("Documentation version:");
|
|
229
|
+
});
|
|
230
|
+
it("should return error for unknown topic", () => {
|
|
231
|
+
const result = readXanoscriptDocsV2(DOCS_PATH, { topic: "nonexistent" });
|
|
232
|
+
expect(result).toContain('Error: Unknown topic "nonexistent"');
|
|
233
|
+
expect(result).toContain("Available topics:");
|
|
234
|
+
});
|
|
235
|
+
it("should return context-aware docs for file_path", () => {
|
|
236
|
+
const result = readXanoscriptDocsV2(DOCS_PATH, {
|
|
237
|
+
file_path: "apis/users/create.xs",
|
|
238
|
+
});
|
|
239
|
+
expect(result).toContain("XanoScript Documentation for: apis/users/create.xs");
|
|
240
|
+
expect(result).toContain("Matched topics:");
|
|
241
|
+
expect(result).toContain("Version:");
|
|
242
|
+
});
|
|
243
|
+
it("should support quick_reference mode for topic", () => {
|
|
244
|
+
const fullResult = readXanoscriptDocsV2(DOCS_PATH, {
|
|
245
|
+
topic: "syntax",
|
|
246
|
+
mode: "full",
|
|
247
|
+
});
|
|
248
|
+
const quickResult = readXanoscriptDocsV2(DOCS_PATH, {
|
|
249
|
+
topic: "syntax",
|
|
250
|
+
mode: "quick_reference",
|
|
251
|
+
});
|
|
252
|
+
// Quick reference should be shorter
|
|
253
|
+
expect(quickResult.length).toBeLessThanOrEqual(fullResult.length);
|
|
254
|
+
});
|
|
255
|
+
it("should support quick_reference mode for file_path", () => {
|
|
256
|
+
const fullResult = readXanoscriptDocsV2(DOCS_PATH, {
|
|
257
|
+
file_path: "apis/test.xs",
|
|
258
|
+
mode: "full",
|
|
259
|
+
});
|
|
260
|
+
const quickResult = readXanoscriptDocsV2(DOCS_PATH, {
|
|
261
|
+
file_path: "apis/test.xs",
|
|
262
|
+
mode: "quick_reference",
|
|
263
|
+
});
|
|
264
|
+
expect(quickResult).toContain("Mode: quick_reference");
|
|
265
|
+
});
|
|
266
|
+
it("should return error for invalid docs path", () => {
|
|
267
|
+
const result = readXanoscriptDocsV2("/nonexistent/path", {
|
|
268
|
+
topic: "syntax",
|
|
269
|
+
});
|
|
270
|
+
expect(result).toContain("Error reading XanoScript documentation:");
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
describe("getTopicNames", () => {
|
|
274
|
+
it("should return all topic names", () => {
|
|
275
|
+
const names = getTopicNames();
|
|
276
|
+
expect(names).toEqual(Object.keys(XANOSCRIPT_DOCS_V2));
|
|
277
|
+
});
|
|
278
|
+
it("should return an array of strings", () => {
|
|
279
|
+
const names = getTopicNames();
|
|
280
|
+
expect(Array.isArray(names)).toBe(true);
|
|
281
|
+
names.forEach((name) => {
|
|
282
|
+
expect(typeof name).toBe("string");
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe("getTopicDescriptions", () => {
|
|
287
|
+
it("should return formatted descriptions string", () => {
|
|
288
|
+
const descriptions = getTopicDescriptions();
|
|
289
|
+
expect(typeof descriptions).toBe("string");
|
|
290
|
+
});
|
|
291
|
+
it("should include all topics", () => {
|
|
292
|
+
const descriptions = getTopicDescriptions();
|
|
293
|
+
for (const key of Object.keys(XANOSCRIPT_DOCS_V2)) {
|
|
294
|
+
expect(descriptions).toContain(key);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
it("should include topic descriptions", () => {
|
|
298
|
+
const descriptions = getTopicDescriptions();
|
|
299
|
+
expect(descriptions).toContain("XanoScript overview");
|
|
300
|
+
expect(descriptions).toContain("Expressions, operators");
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|