@aigne/doc-smith 0.8.11-beta → 0.8.11-beta.2
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/.aigne/doc-smith/config.yaml +2 -0
- package/.aigne/doc-smith/output/structure-plan.json +3 -3
- package/.aigne/doc-smith/upload-cache.yaml +252 -0
- package/.github/workflows/publish-docs.yml +67 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +45 -115
- package/agents/clear/choose-contents.mjs +170 -0
- package/agents/clear/clear-auth-tokens.mjs +111 -0
- package/agents/clear/clear-document-config.mjs +39 -0
- package/agents/clear/clear-document-structure.mjs +106 -0
- package/agents/clear/clear-generated-docs.mjs +51 -0
- package/agents/clear/index.yaml +23 -0
- package/agents/evaluate/code-snippet.mjs +93 -0
- package/agents/evaluate/document-structure.yaml +70 -0
- package/agents/evaluate/document.yaml +79 -0
- package/agents/evaluate/generate-report.mjs +78 -0
- package/agents/evaluate/index.yaml +39 -0
- package/agents/generate/document-structure-tools/add-document.mjs +56 -0
- package/agents/generate/document-structure-tools/delete-document.mjs +49 -0
- package/agents/generate/document-structure-tools/move-document.mjs +82 -0
- package/agents/generate/document-structure-tools/update-document.mjs +50 -0
- package/agents/generate/generate-structure.yaml +1 -1
- package/agents/generate/update-document-structure.yaml +42 -0
- package/agents/generate/user-review-document-structure.mjs +6 -4
- package/agents/init/index.mjs +1 -1
- package/agents/publish/publish-docs.mjs +12 -3
- package/agents/translate/choose-language.mjs +1 -1
- package/agents/update/batch-update-document.yaml +7 -0
- package/agents/update/check-update-is-single.mjs +38 -0
- package/agents/update/document-tools/update-document-content.mjs +293 -0
- package/agents/update/index.yaml +4 -10
- package/agents/update/update-document-detail.yaml +52 -0
- package/agents/update/update-single-document.yaml +15 -0
- package/agents/update/user-review-document.mjs +248 -0
- package/agents/utils/choose-docs.mjs +4 -2
- package/agents/utils/format-document-structure.mjs +12 -2
- package/agents/utils/load-document-all-content.mjs +84 -0
- package/agents/utils/load-sources.mjs +4 -1
- package/aigne.yaml +59 -20
- package/assets/report-template/report.html +198 -0
- package/biome.json +14 -2
- package/docs/advanced-how-it-works.ja.md +101 -0
- package/docs/advanced-how-it-works.zh-TW.md +101 -0
- package/docs/advanced-how-it-works.zh.md +20 -20
- package/docs/advanced-quality-assurance.ja.md +96 -0
- package/docs/advanced-quality-assurance.zh-TW.md +96 -0
- package/docs/advanced-quality-assurance.zh.md +18 -18
- package/docs/advanced.ja.md +16 -0
- package/docs/advanced.zh-TW.md +16 -0
- package/docs/advanced.zh.md +4 -4
- package/docs/changelog.ja.md +309 -0
- package/docs/changelog.zh-TW.md +309 -0
- package/docs/changelog.zh.md +23 -23
- package/docs/cli-reference.ja.md +210 -0
- package/docs/cli-reference.zh-TW.md +210 -0
- package/docs/cli-reference.zh.md +21 -21
- package/docs/configuration-interactive-setup.ja.md +135 -0
- package/docs/configuration-interactive-setup.zh-TW.md +135 -0
- package/docs/configuration-interactive-setup.zh.md +29 -29
- package/docs/configuration-language-support.ja.md +94 -0
- package/docs/configuration-language-support.zh-TW.md +94 -0
- package/docs/configuration-language-support.zh.md +13 -13
- package/docs/configuration-llm-setup.ja.md +54 -0
- package/docs/configuration-llm-setup.zh-TW.md +54 -0
- package/docs/configuration-llm-setup.zh.md +12 -12
- package/docs/configuration-preferences.ja.md +129 -0
- package/docs/configuration-preferences.zh-TW.md +129 -0
- package/docs/configuration-preferences.zh.md +36 -36
- package/docs/configuration.ja.md +172 -0
- package/docs/configuration.zh-TW.md +172 -0
- package/docs/configuration.zh.md +49 -49
- package/docs/features-generate-documentation.ja.md +101 -0
- package/docs/features-generate-documentation.zh-TW.md +101 -0
- package/docs/features-generate-documentation.zh.md +17 -17
- package/docs/features-publish-your-docs.ja.md +107 -0
- package/docs/features-publish-your-docs.zh-TW.md +107 -0
- package/docs/features-publish-your-docs.zh.md +22 -22
- package/docs/features-translate-documentation.ja.md +79 -0
- package/docs/features-translate-documentation.zh-TW.md +79 -0
- package/docs/features-translate-documentation.zh.md +12 -12
- package/docs/features-update-and-refine.ja.md +138 -0
- package/docs/features-update-and-refine.zh-TW.md +138 -0
- package/docs/features-update-and-refine.zh.md +21 -21
- package/docs/features.ja.md +52 -0
- package/docs/features.zh-TW.md +52 -0
- package/docs/features.zh.md +8 -8
- package/docs/getting-started.ja.md +123 -0
- package/docs/getting-started.zh-TW.md +123 -0
- package/docs/getting-started.zh.md +24 -24
- package/docs/overview.ja.md +30 -0
- package/docs/overview.zh-TW.md +30 -0
- package/docs/overview.zh.md +8 -8
- package/package.json +19 -11
- package/prompts/common/document/content-rules-core.md +19 -0
- package/prompts/common/document/media-handling-rules.md +9 -0
- package/prompts/common/document/role-and-personality.md +15 -0
- package/prompts/common/document/user-preferences.md +9 -0
- package/prompts/common/document-structure/conflict-resolution-guidance.md +16 -0
- package/prompts/common/document-structure/document-structure-rules.md +45 -0
- package/prompts/common/document-structure/glossary.md +7 -0
- package/prompts/common/document-structure/intj-traits.md +5 -0
- package/prompts/common/document-structure/output-constraints.md +9 -0
- package/prompts/common/document-structure/user-locale-rules.md +10 -0
- package/prompts/common/document-structure/user-preferences.md +9 -0
- package/prompts/detail/custom/custom-components.md +9 -1
- package/prompts/detail/document-rules.md +6 -6
- package/prompts/detail/generate-document.md +5 -45
- package/prompts/detail/update-document.md +145 -0
- package/prompts/evaluate/document-structure.md +94 -0
- package/prompts/evaluate/document.md +149 -0
- package/prompts/structure/document-rules.md +1 -1
- package/prompts/structure/generate-structure-system.md +74 -0
- package/prompts/structure/generate-structure-user.md +41 -0
- package/prompts/structure/update-document-structure.md +118 -0
- package/prompts/translate/translate-document.md +1 -1
- package/prompts/utils/feedback-refiner.md +3 -3
- package/release-please-config.json +1 -7
- package/tests/agents/clear/choose-contents.test.mjs +280 -0
- package/tests/agents/clear/clear-auth-tokens.test.mjs +268 -0
- package/tests/agents/clear/clear-document-config.test.mjs +167 -0
- package/tests/agents/clear/clear-document-structure.test.mjs +374 -0
- package/tests/agents/clear/clear-generated-docs.test.mjs +222 -0
- package/tests/agents/evaluate/code-snippet.test.mjs +163 -0
- package/tests/agents/evaluate/fixtures/api-services.md +87 -0
- package/tests/agents/evaluate/fixtures/js-sdk.md +94 -0
- package/tests/agents/evaluate/generate-report.test.mjs +312 -0
- package/tests/agents/generate/check-document-structure.test.mjs +0 -6
- package/tests/agents/generate/document-structure-tools/add-document.test.mjs +449 -0
- package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +410 -0
- package/tests/agents/generate/document-structure-tools/move-document.test.mjs +476 -0
- package/tests/agents/generate/document-structure-tools/update-document.test.mjs +548 -0
- package/tests/agents/generate/generate-structure.test.mjs +0 -6
- package/tests/agents/generate/user-review-document-structure.test.mjs +9 -9
- package/tests/agents/publish/publish-docs.test.mjs +2 -2
- package/tests/agents/update/check-update-is-single.test.mjs +300 -0
- package/tests/agents/update/document-tools/update-document-content.test.mjs +326 -0
- package/tests/agents/update/user-review-document.test.mjs +561 -0
- package/tests/agents/utils/format-document-structure.test.mjs +100 -0
- package/tests/utils/auth-utils.test.mjs +239 -1
- package/tests/utils/blocklet.test.mjs +9 -7
- package/tests/utils/constants.test.mjs +1 -1
- package/tests/utils/d2-utils.test.mjs +1 -1
- package/tests/utils/deploy.test.mjs +310 -366
- package/tests/utils/kroki-utils.test.mjs +2 -15
- package/tests/utils/linter/fixtures/css/keyword-error.css +1 -0
- package/tests/utils/linter/fixtures/css/missing-semicolon.css +1 -0
- package/tests/utils/linter/fixtures/css/syntax-error.css +1 -0
- package/tests/utils/linter/fixtures/css/undeclare-variable.css +1 -0
- package/tests/utils/linter/fixtures/css/unused-variable.css +2 -0
- package/tests/utils/linter/fixtures/css/valid-code.css +1 -0
- package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +1 -0
- package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +2 -0
- package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +2 -0
- package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +1 -0
- package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +1 -0
- package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +2 -0
- package/tests/utils/linter/fixtures/go/keyword-error.go +5 -0
- package/tests/utils/linter/fixtures/go/missing-semicolon.go +5 -0
- package/tests/utils/linter/fixtures/go/syntax-error.go +6 -0
- package/tests/utils/linter/fixtures/go/undeclare-variable.go +5 -0
- package/tests/utils/linter/fixtures/go/unused-variable.go +5 -0
- package/tests/utils/linter/fixtures/go/valid-code.go +7 -0
- package/tests/utils/linter/fixtures/js/keyword-error.js +3 -0
- package/tests/utils/linter/fixtures/js/missing-semicolon.js +6 -0
- package/tests/utils/linter/fixtures/js/syntax-error.js +4 -0
- package/tests/utils/linter/fixtures/js/undeclare-variable.js +3 -0
- package/tests/utils/linter/fixtures/js/unused-variable.js +7 -0
- package/tests/utils/linter/fixtures/js/valid-code.js +15 -0
- package/tests/utils/linter/fixtures/json/keyword-error.json +1 -0
- package/tests/utils/linter/fixtures/json/missing-semicolon.json +1 -0
- package/tests/utils/linter/fixtures/json/syntax-error.json +1 -0
- package/tests/utils/linter/fixtures/json/undeclare-variable.json +1 -0
- package/tests/utils/linter/fixtures/json/unused-variable.json +1 -0
- package/tests/utils/linter/fixtures/json/valid-code.json +1 -0
- package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +5 -0
- package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +4 -0
- package/tests/utils/linter/fixtures/jsx/valid-code.jsx +5 -0
- package/tests/utils/linter/fixtures/python/keyword-error.py +3 -0
- package/tests/utils/linter/fixtures/python/missing-semicolon.py +2 -0
- package/tests/utils/linter/fixtures/python/syntax-error.py +3 -0
- package/tests/utils/linter/fixtures/python/undeclare-variable.py +3 -0
- package/tests/utils/linter/fixtures/python/unused-variable.py +6 -0
- package/tests/utils/linter/fixtures/python/valid-code.py +12 -0
- package/tests/utils/linter/fixtures/ruby/keyword-error.rb +2 -0
- package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +1 -0
- package/tests/utils/linter/fixtures/ruby/syntax-error.rb +2 -0
- package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +1 -0
- package/tests/utils/linter/fixtures/ruby/unused-variable.rb +2 -0
- package/tests/utils/linter/fixtures/ruby/valid-code.rb +1 -0
- package/tests/utils/linter/fixtures/sass/keyword-error.sass +2 -0
- package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +3 -0
- package/tests/utils/linter/fixtures/sass/syntax-error.sass +3 -0
- package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +2 -0
- package/tests/utils/linter/fixtures/sass/unused-variable.sass +4 -0
- package/tests/utils/linter/fixtures/sass/valid-code.sass +2 -0
- package/tests/utils/linter/fixtures/scss/keyword-error.scss +1 -0
- package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +1 -0
- package/tests/utils/linter/fixtures/scss/syntax-error.scss +1 -0
- package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +1 -0
- package/tests/utils/linter/fixtures/scss/unused-variable.scss +2 -0
- package/tests/utils/linter/fixtures/scss/valid-code.scss +1 -0
- package/tests/utils/linter/fixtures/shell/keyword-error.sh +5 -0
- package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +3 -0
- package/tests/utils/linter/fixtures/shell/syntax-error.sh +4 -0
- package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +3 -0
- package/tests/utils/linter/fixtures/shell/unused-variable.sh +4 -0
- package/tests/utils/linter/fixtures/shell/valid-code.sh +3 -0
- package/tests/utils/linter/fixtures/ts/keyword-error.ts +1 -0
- package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +1 -0
- package/tests/utils/linter/fixtures/ts/syntax-error.ts +1 -0
- package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +1 -0
- package/tests/utils/linter/fixtures/ts/unused-variable.ts +3 -0
- package/tests/utils/linter/fixtures/ts/valid-code.ts +3 -0
- package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +5 -0
- package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +5 -0
- package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +5 -0
- package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +6 -0
- package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +6 -0
- package/tests/utils/linter/fixtures/tsx/valid-code.tsx +5 -0
- package/tests/utils/linter/fixtures/vue/keyword-error.vue +6 -0
- package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +6 -0
- package/tests/utils/linter/fixtures/vue/syntax-error.vue +6 -0
- package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +6 -0
- package/tests/utils/linter/fixtures/vue/unused-variable.vue +7 -0
- package/tests/utils/linter/fixtures/vue/valid-code.vue +6 -0
- package/tests/utils/linter/fixtures/yaml/keyword-error.yml +1 -0
- package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +2 -0
- package/tests/utils/linter/fixtures/yaml/syntax-error.yml +1 -0
- package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +1 -0
- package/tests/utils/linter/fixtures/yaml/unused-variable.yml +2 -0
- package/tests/utils/linter/fixtures/yaml/valid-code.yml +3 -0
- package/tests/utils/linter/index.test.mjs +440 -0
- package/tests/utils/linter/scan-results.mjs +42 -0
- package/tests/utils/markdown/index.test.mjs +478 -0
- package/tests/utils/mermaid-validator.test.mjs +2 -2
- package/tests/utils/utils.test.mjs +3 -1
- package/types/document-schema.mjs +54 -0
- package/types/document-structure-schema.mjs +244 -0
- package/utils/auth-utils.mjs +131 -6
- package/utils/conflict-detector.mjs +5 -1
- package/utils/{constants.mjs → constants/index.mjs} +109 -0
- package/utils/constants/linter.mjs +102 -0
- package/utils/d2-utils.mjs +2 -4
- package/utils/debug.mjs +3 -0
- package/utils/deploy.mjs +81 -385
- package/utils/evaluate/report-utils.mjs +131 -0
- package/utils/file-utils.mjs +36 -1
- package/utils/kroki-utils.mjs +1 -1
- package/utils/linter/index.mjs +50 -0
- package/utils/markdown/index.mjs +26 -0
- package/utils/markdown-checker.mjs +1 -1
- package/utils/utils.mjs +19 -7
- package/prompts/structure/generate-structure.md +0 -161
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
2
|
+
import * as fsPromises from "node:fs/promises";
|
|
3
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import clearGeneratedDocs from "../../../agents/clear/clear-generated-docs.mjs";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
describe("clear-generated-docs", () => {
|
|
11
|
+
let testDir;
|
|
12
|
+
let docsDir;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
// Create a temporary test directory
|
|
16
|
+
testDir = join(__dirname, "test-clear-docs");
|
|
17
|
+
await mkdir(testDir, { recursive: true });
|
|
18
|
+
|
|
19
|
+
docsDir = join(testDir, "docs");
|
|
20
|
+
await mkdir(docsDir, { recursive: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
// Clean up test directory
|
|
25
|
+
try {
|
|
26
|
+
await rm(testDir, { recursive: true, force: true });
|
|
27
|
+
} catch {
|
|
28
|
+
// Ignore cleanup errors since they don't affect test results
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("should clear existing generated documents successfully", async () => {
|
|
33
|
+
// Create some test files in docs directory
|
|
34
|
+
const testFiles = ["index.md", "guide.md", "api.md", "README.md"];
|
|
35
|
+
for (const file of testFiles) {
|
|
36
|
+
await writeFile(join(docsDir, file), `# ${file} content`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create subdirectories with files
|
|
40
|
+
const subDir = join(docsDir, "advanced");
|
|
41
|
+
await mkdir(subDir, { recursive: true });
|
|
42
|
+
await writeFile(join(subDir, "config.md"), "# Config content");
|
|
43
|
+
|
|
44
|
+
const result = await clearGeneratedDocs({ docsDir });
|
|
45
|
+
|
|
46
|
+
expect(result.cleared).toBe(true);
|
|
47
|
+
expect(result.message).toContain("Cleared generated documents");
|
|
48
|
+
expect(result.path).toBeDefined();
|
|
49
|
+
|
|
50
|
+
// Verify directory is actually deleted
|
|
51
|
+
const { pathExists } = await import("../../../utils/file-utils.mjs");
|
|
52
|
+
const exists = await pathExists(docsDir);
|
|
53
|
+
expect(exists).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("should handle non-existent documents directory", async () => {
|
|
57
|
+
const nonExistentDir = join(testDir, "non-existent-docs");
|
|
58
|
+
|
|
59
|
+
const result = await clearGeneratedDocs({ docsDir: nonExistentDir });
|
|
60
|
+
|
|
61
|
+
expect(result.cleared).toBe(false);
|
|
62
|
+
expect(result.message).toContain("Generated documents already empty");
|
|
63
|
+
expect(result.path).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("should return error message when no docsDir provided", async () => {
|
|
67
|
+
const result = await clearGeneratedDocs({});
|
|
68
|
+
|
|
69
|
+
expect(result.message).toBe("No generated documents directory specified");
|
|
70
|
+
expect(result).not.toHaveProperty("cleared");
|
|
71
|
+
expect(result).not.toHaveProperty("path");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("should handle null docsDir", async () => {
|
|
75
|
+
const result = await clearGeneratedDocs({ docsDir: null });
|
|
76
|
+
|
|
77
|
+
expect(result.message).toBe("No generated documents directory specified");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("should handle empty string docsDir", async () => {
|
|
81
|
+
const result = await clearGeneratedDocs({ docsDir: "" });
|
|
82
|
+
|
|
83
|
+
expect(result.message).toBe("No generated documents directory specified");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("should handle undefined docsDir", async () => {
|
|
87
|
+
const result = await clearGeneratedDocs({ docsDir: undefined });
|
|
88
|
+
|
|
89
|
+
expect(result.message).toBe("No generated documents directory specified");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("should provide correct return structure", async () => {
|
|
93
|
+
await writeFile(join(docsDir, "test.md"), "test content");
|
|
94
|
+
|
|
95
|
+
const result = await clearGeneratedDocs({ docsDir });
|
|
96
|
+
|
|
97
|
+
expect(result).toHaveProperty("message");
|
|
98
|
+
expect(result).toHaveProperty("cleared");
|
|
99
|
+
expect(result).toHaveProperty("path");
|
|
100
|
+
expect(typeof result.message).toBe("string");
|
|
101
|
+
expect(typeof result.cleared).toBe("boolean");
|
|
102
|
+
expect(typeof result.path).toBe("string");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("should have correct input schema", () => {
|
|
106
|
+
expect(clearGeneratedDocs.input_schema).toBeDefined();
|
|
107
|
+
expect(clearGeneratedDocs.input_schema.type).toBe("object");
|
|
108
|
+
expect(clearGeneratedDocs.input_schema.properties.docsDir).toBeDefined();
|
|
109
|
+
expect(clearGeneratedDocs.input_schema.properties.docsDir.type).toBe("string");
|
|
110
|
+
expect(clearGeneratedDocs.input_schema.properties.docsDir.description).toBe(
|
|
111
|
+
"The generated documents directory to clear",
|
|
112
|
+
);
|
|
113
|
+
expect(clearGeneratedDocs.input_schema.required).toEqual(["docsDir"]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("should have correct task metadata", () => {
|
|
117
|
+
expect(clearGeneratedDocs.taskTitle).toBe("Clear all generated documents");
|
|
118
|
+
expect(clearGeneratedDocs.description).toBe("Clear the generated documents directory");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("should handle relative paths", async () => {
|
|
122
|
+
// Create test files
|
|
123
|
+
await writeFile(join(docsDir, "test.md"), "test content");
|
|
124
|
+
|
|
125
|
+
// Use relative path
|
|
126
|
+
const relativePath = "./docs";
|
|
127
|
+
const originalCwd = process.cwd();
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
process.chdir(testDir);
|
|
131
|
+
const result = await clearGeneratedDocs({ docsDir: relativePath });
|
|
132
|
+
|
|
133
|
+
expect(result.cleared).toBe(true);
|
|
134
|
+
expect(result.message).toContain("Cleared generated documents");
|
|
135
|
+
} finally {
|
|
136
|
+
process.chdir(originalCwd);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("should handle absolute paths", async () => {
|
|
141
|
+
// Create test files
|
|
142
|
+
await writeFile(join(docsDir, "test.md"), "test content");
|
|
143
|
+
|
|
144
|
+
const result = await clearGeneratedDocs({ docsDir });
|
|
145
|
+
|
|
146
|
+
expect(result.cleared).toBe(true);
|
|
147
|
+
expect(result.message).toContain("Cleared generated documents");
|
|
148
|
+
|
|
149
|
+
// Verify absolute path works
|
|
150
|
+
const { pathExists } = await import("../../../utils/file-utils.mjs");
|
|
151
|
+
const exists = await pathExists(docsDir);
|
|
152
|
+
expect(exists).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("should handle complex directory structures", async () => {
|
|
156
|
+
// Create complex nested structure
|
|
157
|
+
const nestedDirs = [
|
|
158
|
+
join(docsDir, "api"),
|
|
159
|
+
join(docsDir, "guides", "getting-started"),
|
|
160
|
+
join(docsDir, "tutorials", "advanced"),
|
|
161
|
+
join(docsDir, ".hidden"),
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
for (const dir of nestedDirs) {
|
|
165
|
+
await mkdir(dir, { recursive: true });
|
|
166
|
+
await writeFile(join(dir, "content.md"), "nested content");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Create files at root level
|
|
170
|
+
await writeFile(join(docsDir, "index.md"), "root content");
|
|
171
|
+
await writeFile(join(docsDir, ".gitignore"), "node_modules/");
|
|
172
|
+
|
|
173
|
+
const result = await clearGeneratedDocs({ docsDir });
|
|
174
|
+
|
|
175
|
+
expect(result.cleared).toBe(true);
|
|
176
|
+
expect(result.message).toContain("Cleared generated documents");
|
|
177
|
+
|
|
178
|
+
// Verify entire structure is deleted
|
|
179
|
+
const { pathExists } = await import("../../../utils/file-utils.mjs");
|
|
180
|
+
const exists = await pathExists(docsDir);
|
|
181
|
+
expect(exists).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("should handle paths with special characters", async () => {
|
|
185
|
+
// Create directory with special characters
|
|
186
|
+
const specialDocsDir = join(testDir, "docs with spaces & symbols-123");
|
|
187
|
+
await mkdir(specialDocsDir, { recursive: true });
|
|
188
|
+
await writeFile(join(specialDocsDir, "test file.md"), "special content");
|
|
189
|
+
|
|
190
|
+
const result = await clearGeneratedDocs({ docsDir: specialDocsDir });
|
|
191
|
+
|
|
192
|
+
expect(result.cleared).toBe(true);
|
|
193
|
+
expect(result.message).toContain("Cleared generated documents");
|
|
194
|
+
|
|
195
|
+
// Verify special path is deleted
|
|
196
|
+
const { pathExists } = await import("../../../utils/file-utils.mjs");
|
|
197
|
+
const exists = await pathExists(specialDocsDir);
|
|
198
|
+
expect(exists).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("should handle file system errors gracefully", async () => {
|
|
202
|
+
// Create some test files first
|
|
203
|
+
await writeFile(join(docsDir, "test.md"), "test content");
|
|
204
|
+
|
|
205
|
+
// Create a spy on rm to simulate an error
|
|
206
|
+
const rmSpy = spyOn(fsPromises, "rm");
|
|
207
|
+
rmSpy.mockImplementation(() => {
|
|
208
|
+
throw new Error("Permission denied");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const result = await clearGeneratedDocs({ docsDir });
|
|
213
|
+
|
|
214
|
+
expect(result.error).toBe(true);
|
|
215
|
+
expect(result.message).toContain("Failed to clear generated documents");
|
|
216
|
+
expect(result.message).toContain("Permission denied");
|
|
217
|
+
expect(result.path).toBeDefined();
|
|
218
|
+
} finally {
|
|
219
|
+
rmSpy.mockRestore();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path, { dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import evaluateDocumentCode from "../../../agents/evaluate/code-snippet.mjs";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
describe("evaluateDocumentCode", () => {
|
|
12
|
+
test(
|
|
13
|
+
"should evaluate markdown document",
|
|
14
|
+
async () => {
|
|
15
|
+
const content = await fs.readFile(path.join(__dirname, "fixtures/js-sdk.md"), "utf-8");
|
|
16
|
+
const result = await evaluateDocumentCode({ content });
|
|
17
|
+
|
|
18
|
+
expect("codeEvaluation" in result).toEqual(true);
|
|
19
|
+
expect(result.codeEvaluation.baseline).toEqual(100);
|
|
20
|
+
expect("details" in result.codeEvaluation).toEqual(true);
|
|
21
|
+
expect(result.codeEvaluation.details.length).toEqual(0);
|
|
22
|
+
expect(result.codeEvaluation.totalCount).toEqual(3);
|
|
23
|
+
expect(result.codeEvaluation.errorCount).toEqual(0);
|
|
24
|
+
expect(result.codeEvaluation.ignoreCount).toEqual(0);
|
|
25
|
+
},
|
|
26
|
+
60 * 1000,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
test(
|
|
30
|
+
"should evaluate markdown document with error",
|
|
31
|
+
async () => {
|
|
32
|
+
const content = await fs.readFile(path.join(__dirname, "fixtures/api-services.md"), "utf-8");
|
|
33
|
+
const result = await evaluateDocumentCode({ content });
|
|
34
|
+
|
|
35
|
+
expect(result.codeEvaluation.details.length).toEqual(2);
|
|
36
|
+
expect(result.codeEvaluation.totalCount).toEqual(1);
|
|
37
|
+
expect(result.codeEvaluation.errorCount).toEqual(1);
|
|
38
|
+
expect(result.codeEvaluation.ignoreCount).toEqual(1);
|
|
39
|
+
},
|
|
40
|
+
60 * 1000,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
test(
|
|
44
|
+
"should handle linter failures with retry mechanism",
|
|
45
|
+
async () => {
|
|
46
|
+
// Since we can't easily mock ES modules, we'll mock the global fetch used by lintCode
|
|
47
|
+
const originalFetch = globalThis.fetch;
|
|
48
|
+
let callCount = 0;
|
|
49
|
+
|
|
50
|
+
// Mock fetch to simulate linter API failures
|
|
51
|
+
globalThis.fetch = async (_url, _options) => {
|
|
52
|
+
callCount++;
|
|
53
|
+
if (callCount <= 2) {
|
|
54
|
+
// Fail the first 2 attempts
|
|
55
|
+
throw new Error(`Network error ${callCount}`);
|
|
56
|
+
}
|
|
57
|
+
// Succeed on the third attempt with a valid response
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
json: async () => ({
|
|
61
|
+
success: true,
|
|
62
|
+
issues: [],
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Create markdown content with code blocks that will trigger linting
|
|
69
|
+
const content = `
|
|
70
|
+
# Test Document
|
|
71
|
+
|
|
72
|
+
\`\`\`javascript
|
|
73
|
+
console.log("test");
|
|
74
|
+
\`\`\`
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const result = await evaluateDocumentCode({ content });
|
|
78
|
+
|
|
79
|
+
// Verify the function completed successfully despite initial failures
|
|
80
|
+
expect(result.codeEvaluation).toBeDefined();
|
|
81
|
+
expect(result.codeEvaluation.totalCount).toBe(1);
|
|
82
|
+
|
|
83
|
+
// With fetch mocking, if retries work, we should get a successful result
|
|
84
|
+
// The exact behavior depends on how the retries are handled
|
|
85
|
+
} finally {
|
|
86
|
+
// Restore the original fetch function
|
|
87
|
+
globalThis.fetch = originalFetch;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
120 * 1000, // Longer timeout for retry testing
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
test(
|
|
94
|
+
"should handle persistent linter failures after retries",
|
|
95
|
+
async () => {
|
|
96
|
+
// Mock fetch to always fail
|
|
97
|
+
const originalFetch = globalThis.fetch;
|
|
98
|
+
let callCount = 0;
|
|
99
|
+
|
|
100
|
+
globalThis.fetch = async (_url, _options) => {
|
|
101
|
+
callCount++;
|
|
102
|
+
throw new Error(`Persistent network failure ${callCount}`);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const content = `
|
|
107
|
+
# Test Document
|
|
108
|
+
|
|
109
|
+
\`\`\`javascript
|
|
110
|
+
console.log("test");
|
|
111
|
+
\`\`\`
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
// Expect this to throw since all retries will fail
|
|
115
|
+
await expect(evaluateDocumentCode({ content })).rejects.toThrow("Linting failed:");
|
|
116
|
+
|
|
117
|
+
// Verify retries happened (should call 4 times total: 1 + 3 retries)
|
|
118
|
+
expect(callCount).toBe(4);
|
|
119
|
+
} finally {
|
|
120
|
+
// Restore the original fetch function
|
|
121
|
+
globalThis.fetch = originalFetch;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
120 * 1000,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
test("should demonstrate onFailedAttempt callback logic from lines 42-44", () => {
|
|
128
|
+
// Test that simulates the exact logic from the onFailedAttempt callback (lines 42-44)
|
|
129
|
+
// This verifies that the callback would format the debug message correctly
|
|
130
|
+
|
|
131
|
+
// Simulate the parameters that would be passed to onFailedAttempt
|
|
132
|
+
const mockFailedAttempt = {
|
|
133
|
+
error: new Error("Simulated linter failure"),
|
|
134
|
+
attemptNumber: 2,
|
|
135
|
+
retriesLeft: 1,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Simulate the exact logic from lines 43-44 in the source code
|
|
139
|
+
const debugMessage = `Attempt ${mockFailedAttempt.attemptNumber} failed: ${mockFailedAttempt.error.message}. There are ${mockFailedAttempt.retriesLeft} retries left.`;
|
|
140
|
+
|
|
141
|
+
// Verify the message format matches what's expected from the source
|
|
142
|
+
const expectedMessage = "Attempt 2 failed: Simulated linter failure. There are 1 retries left.";
|
|
143
|
+
expect(debugMessage).toBe(expectedMessage);
|
|
144
|
+
|
|
145
|
+
// Verify the message format follows the pattern from lines 43-44
|
|
146
|
+
const messagePattern = /Attempt \d+ failed: .+\. There are \d+ retries left\./;
|
|
147
|
+
expect(debugMessage).toMatch(messagePattern);
|
|
148
|
+
|
|
149
|
+
// Test with different retry scenarios
|
|
150
|
+
const scenarios = [
|
|
151
|
+
{ attemptNumber: 1, retriesLeft: 2, error: new Error("First failure") },
|
|
152
|
+
{ attemptNumber: 3, retriesLeft: 0, error: new Error("Final attempt") },
|
|
153
|
+
{ attemptNumber: 2, retriesLeft: 1, error: new Error("Network timeout") },
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
scenarios.forEach((scenario) => {
|
|
157
|
+
const message = `Attempt ${scenario.attemptNumber} failed: ${scenario.error.message}. There are ${scenario.retriesLeft} retries left.`;
|
|
158
|
+
expect(message).toMatch(messagePattern);
|
|
159
|
+
expect(message).toContain(`Attempt ${scenario.attemptNumber} failed:`);
|
|
160
|
+
expect(message).toContain(`There are ${scenario.retriesLeft} retries left.`);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Services
|
|
2
|
+
|
|
3
|
+
The `@blocklet/js-sdk` is organized into several service classes, each responsible for a specific domain of functionality. These services act as dedicated clients for different Blocklet API endpoints, providing a structured and intuitive way to interact with the platform's features.
|
|
4
|
+
|
|
5
|
+
Most core services are pre-initialized and available as properties on the main `BlockletSDK` instance, which you can obtain using the `getBlockletSDK()` function.
|
|
6
|
+
|
|
7
|
+
```javascript Accessing a Service icon=logos:javascript
|
|
8
|
+
import { getBlockletSDK } from '@blocklet/js-sdk';
|
|
9
|
+
|
|
10
|
+
const sdk = getBlockletSDK();
|
|
11
|
+
|
|
12
|
+
// Access the AuthService to get user info
|
|
13
|
+
async function getUserProfile() {
|
|
14
|
+
const profile = await sdk.user.getProfile();
|
|
15
|
+
console.log(profile);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Access the BlockletService to get blocklet info
|
|
19
|
+
async function getBlockletMeta() {
|
|
20
|
+
const meta = await sdk.blocklet.getMeta();
|
|
21
|
+
console.log(meta);
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The diagram below illustrates the structure of the `BlockletSDK` instance and its relationship with the core services.
|
|
26
|
+
|
|
27
|
+
```d2
|
|
28
|
+
direction: down
|
|
29
|
+
|
|
30
|
+
BlockletSDK: {
|
|
31
|
+
label: "BlockletSDK Instance"
|
|
32
|
+
shape: rectangle
|
|
33
|
+
|
|
34
|
+
grid-columns: 3
|
|
35
|
+
grid-gap: 50
|
|
36
|
+
|
|
37
|
+
user: {
|
|
38
|
+
label: "user: AuthService"
|
|
39
|
+
shape: rectangle
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
blocklet: {
|
|
43
|
+
label: "blocklet: BlockletService"
|
|
44
|
+
shape: rectangle
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
userSession: {
|
|
48
|
+
label: "userSession: UserSessionService"
|
|
49
|
+
shape: rectangle
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
federated: {
|
|
53
|
+
label: "federated: FederatedService"
|
|
54
|
+
shape: rectangle
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
token: {
|
|
58
|
+
label: "token: TokenService"
|
|
59
|
+
shape: rectangle
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Below is a complete list of the available services. Click on any service to view its detailed API reference.
|
|
65
|
+
|
|
66
|
+
<x-cards data-columns="2">
|
|
67
|
+
<x-card data-title="AuthService" data-href="/api/services/auth" data-icon="lucide:user-cog">
|
|
68
|
+
API for managing user profiles, privacy settings, notifications, and authentication actions like logout.
|
|
69
|
+
</x-card>
|
|
70
|
+
<x-card data-title="BlockletService" data-href="/api/services/blocklet" data-icon="lucide:box">
|
|
71
|
+
API for fetching and loading blocklet metadata from `window.blocklet` or a remote URL.
|
|
72
|
+
</x-card>
|
|
73
|
+
<x-card data-title="ComponentService" data-href="/api/services/component" data-icon="lucide:layout-template">
|
|
74
|
+
API for getting information about mounted components and constructing URLs for them.
|
|
75
|
+
</x-card>
|
|
76
|
+
<x-card data-title="FederatedService" data-href="/api/services/federated" data-icon="lucide:network">
|
|
77
|
+
API for interacting with Federated Login Group settings and retrieving information about master and current apps.
|
|
78
|
+
</x-card>
|
|
79
|
+
<x-card data-title="TokenService" data-href="/api/services/token" data-icon="lucide:key-round">
|
|
80
|
+
Low-level API for getting, setting, and removing session and refresh tokens from storage (Cookies and LocalStorage).
|
|
81
|
+
</x-card>
|
|
82
|
+
<x-card data-title="UserSessionService" data-href="/api/services/user-session" data-icon="lucide:users">
|
|
83
|
+
API for fetching and managing user login sessions.
|
|
84
|
+
</x-card>
|
|
85
|
+
</x-cards>
|
|
86
|
+
|
|
87
|
+
Each service provides a focused set of methods for a specific part of the Blocklet platform. To understand the data structures and types returned by these services, please see the [Types](./api-types.md) reference.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This guide will walk you through the essential steps to install the `@blocklet/js-sdk` and make your first API call. Our goal is to get you up and running in just a few minutes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
First, you need to add the SDK to your project. You can use your preferred package manager:
|
|
8
|
+
|
|
9
|
+
```bash Installation icon=mdi:bash
|
|
10
|
+
npm install @blocklet/js-sdk
|
|
11
|
+
|
|
12
|
+
# or
|
|
13
|
+
|
|
14
|
+
yarn add @blocklet/js-sdk
|
|
15
|
+
|
|
16
|
+
# or
|
|
17
|
+
|
|
18
|
+
pnpm add @blocklet/js-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
The SDK is designed to be straightforward. The two most common use cases are accessing core Blocklet services (like user authentication) and making authenticated requests to your own Blocklet's backend.
|
|
24
|
+
|
|
25
|
+
### Accessing Core Services
|
|
26
|
+
|
|
27
|
+
The easiest way to use the SDK is by importing the `getBlockletSDK` singleton factory. This function ensures that you always get the same SDK instance throughout your application, simplifying state management.
|
|
28
|
+
|
|
29
|
+
Here's how you can use it to fetch the current user's profile:
|
|
30
|
+
|
|
31
|
+
```javascript Get User Profile icon=logos:javascript
|
|
32
|
+
import { getBlockletSDK } from '@blocklet/js-sdk';
|
|
33
|
+
|
|
34
|
+
const sdk = getBlockletSDK();
|
|
35
|
+
|
|
36
|
+
async function fetchUserProfile() {
|
|
37
|
+
try {
|
|
38
|
+
const { data: userProfile } = await sdk.user.getProfile();
|
|
39
|
+
console.log('User Profile:', userProfile);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Failed to fetch user profile:', error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fetchUserProfile();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `sdk` instance provides access to various services like `user`, `userSession`, `blocklet`, and more. These services handle communication with the well-known Blocklet service endpoints for you.
|
|
49
|
+
|
|
50
|
+
### Making API Requests to Your Blocklet
|
|
51
|
+
|
|
52
|
+
For communicating with your own Blocklet's backend API, the SDK provides `createAxios` and `createFetch` helper functions. These are wrappers around Axios and the native Fetch API that come pre-configured with everything needed for authenticated requests.
|
|
53
|
+
|
|
54
|
+
They automatically handle:
|
|
55
|
+
- Setting the correct `baseURL` for your component.
|
|
56
|
+
- Attaching the session token to the `Authorization` header.
|
|
57
|
+
- Including the `x-csrf-token` for security.
|
|
58
|
+
- Refreshing the session token automatically if it expires.
|
|
59
|
+
|
|
60
|
+
Here’s how to create an API client for your backend using `createAxios`:
|
|
61
|
+
|
|
62
|
+
```javascript Create an API Client icon=logos:javascript
|
|
63
|
+
import { createAxios } from '@blocklet/js-sdk';
|
|
64
|
+
|
|
65
|
+
// Create an Axios instance configured for your Blocklet
|
|
66
|
+
const apiClient = createAxios();
|
|
67
|
+
|
|
68
|
+
async function fetchData() {
|
|
69
|
+
try {
|
|
70
|
+
// Make a request to your own backend, e.g., GET /api/posts
|
|
71
|
+
const response = await apiClient.get('/api/posts');
|
|
72
|
+
console.log('Posts:', response.data);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('Failed to fetch data:', error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fetchData();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
With this setup, you don't need to manually manage tokens or headers. The SDK takes care of the authentication flow seamlessly.
|
|
82
|
+
|
|
83
|
+
## Next Steps
|
|
84
|
+
|
|
85
|
+
You've now learned how to install the `@blocklet/js-sdk` and use it for the two most common scenarios. To dive deeper, we recommend exploring the following guides:
|
|
86
|
+
|
|
87
|
+
<x-cards>
|
|
88
|
+
<x-card data-title="Making API Requests" data-icon="lucide:file-code-2" data-href="/guides/making-api-requests">
|
|
89
|
+
Learn more about advanced configuration for `createAxios` and `createFetch`, including error handling and request parameters.
|
|
90
|
+
</x-card>
|
|
91
|
+
<x-card data-title="Authentication" data-icon="lucide:key-round" data-href="/guides/authentication">
|
|
92
|
+
Understand how the SDK manages session and refresh tokens under the hood to keep your users logged in.
|
|
93
|
+
</x-card>
|
|
94
|
+
</x-cards>
|