@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.
Files changed (257) hide show
  1. package/.aigne/doc-smith/config.yaml +2 -0
  2. package/.aigne/doc-smith/output/structure-plan.json +3 -3
  3. package/.aigne/doc-smith/upload-cache.yaml +252 -0
  4. package/.github/workflows/publish-docs.yml +67 -0
  5. package/.release-please-manifest.json +1 -1
  6. package/CHANGELOG.md +22 -0
  7. package/README.md +45 -115
  8. package/agents/clear/choose-contents.mjs +170 -0
  9. package/agents/clear/clear-auth-tokens.mjs +111 -0
  10. package/agents/clear/clear-document-config.mjs +39 -0
  11. package/agents/clear/clear-document-structure.mjs +106 -0
  12. package/agents/clear/clear-generated-docs.mjs +51 -0
  13. package/agents/clear/index.yaml +23 -0
  14. package/agents/evaluate/code-snippet.mjs +93 -0
  15. package/agents/evaluate/document-structure.yaml +70 -0
  16. package/agents/evaluate/document.yaml +79 -0
  17. package/agents/evaluate/generate-report.mjs +78 -0
  18. package/agents/evaluate/index.yaml +39 -0
  19. package/agents/generate/document-structure-tools/add-document.mjs +56 -0
  20. package/agents/generate/document-structure-tools/delete-document.mjs +49 -0
  21. package/agents/generate/document-structure-tools/move-document.mjs +82 -0
  22. package/agents/generate/document-structure-tools/update-document.mjs +50 -0
  23. package/agents/generate/generate-structure.yaml +1 -1
  24. package/agents/generate/update-document-structure.yaml +42 -0
  25. package/agents/generate/user-review-document-structure.mjs +6 -4
  26. package/agents/init/index.mjs +1 -1
  27. package/agents/publish/publish-docs.mjs +12 -3
  28. package/agents/translate/choose-language.mjs +1 -1
  29. package/agents/update/batch-update-document.yaml +7 -0
  30. package/agents/update/check-update-is-single.mjs +38 -0
  31. package/agents/update/document-tools/update-document-content.mjs +293 -0
  32. package/agents/update/index.yaml +4 -10
  33. package/agents/update/update-document-detail.yaml +52 -0
  34. package/agents/update/update-single-document.yaml +15 -0
  35. package/agents/update/user-review-document.mjs +248 -0
  36. package/agents/utils/choose-docs.mjs +4 -2
  37. package/agents/utils/format-document-structure.mjs +12 -2
  38. package/agents/utils/load-document-all-content.mjs +84 -0
  39. package/agents/utils/load-sources.mjs +4 -1
  40. package/aigne.yaml +59 -20
  41. package/assets/report-template/report.html +198 -0
  42. package/biome.json +14 -2
  43. package/docs/advanced-how-it-works.ja.md +101 -0
  44. package/docs/advanced-how-it-works.zh-TW.md +101 -0
  45. package/docs/advanced-how-it-works.zh.md +20 -20
  46. package/docs/advanced-quality-assurance.ja.md +96 -0
  47. package/docs/advanced-quality-assurance.zh-TW.md +96 -0
  48. package/docs/advanced-quality-assurance.zh.md +18 -18
  49. package/docs/advanced.ja.md +16 -0
  50. package/docs/advanced.zh-TW.md +16 -0
  51. package/docs/advanced.zh.md +4 -4
  52. package/docs/changelog.ja.md +309 -0
  53. package/docs/changelog.zh-TW.md +309 -0
  54. package/docs/changelog.zh.md +23 -23
  55. package/docs/cli-reference.ja.md +210 -0
  56. package/docs/cli-reference.zh-TW.md +210 -0
  57. package/docs/cli-reference.zh.md +21 -21
  58. package/docs/configuration-interactive-setup.ja.md +135 -0
  59. package/docs/configuration-interactive-setup.zh-TW.md +135 -0
  60. package/docs/configuration-interactive-setup.zh.md +29 -29
  61. package/docs/configuration-language-support.ja.md +94 -0
  62. package/docs/configuration-language-support.zh-TW.md +94 -0
  63. package/docs/configuration-language-support.zh.md +13 -13
  64. package/docs/configuration-llm-setup.ja.md +54 -0
  65. package/docs/configuration-llm-setup.zh-TW.md +54 -0
  66. package/docs/configuration-llm-setup.zh.md +12 -12
  67. package/docs/configuration-preferences.ja.md +129 -0
  68. package/docs/configuration-preferences.zh-TW.md +129 -0
  69. package/docs/configuration-preferences.zh.md +36 -36
  70. package/docs/configuration.ja.md +172 -0
  71. package/docs/configuration.zh-TW.md +172 -0
  72. package/docs/configuration.zh.md +49 -49
  73. package/docs/features-generate-documentation.ja.md +101 -0
  74. package/docs/features-generate-documentation.zh-TW.md +101 -0
  75. package/docs/features-generate-documentation.zh.md +17 -17
  76. package/docs/features-publish-your-docs.ja.md +107 -0
  77. package/docs/features-publish-your-docs.zh-TW.md +107 -0
  78. package/docs/features-publish-your-docs.zh.md +22 -22
  79. package/docs/features-translate-documentation.ja.md +79 -0
  80. package/docs/features-translate-documentation.zh-TW.md +79 -0
  81. package/docs/features-translate-documentation.zh.md +12 -12
  82. package/docs/features-update-and-refine.ja.md +138 -0
  83. package/docs/features-update-and-refine.zh-TW.md +138 -0
  84. package/docs/features-update-and-refine.zh.md +21 -21
  85. package/docs/features.ja.md +52 -0
  86. package/docs/features.zh-TW.md +52 -0
  87. package/docs/features.zh.md +8 -8
  88. package/docs/getting-started.ja.md +123 -0
  89. package/docs/getting-started.zh-TW.md +123 -0
  90. package/docs/getting-started.zh.md +24 -24
  91. package/docs/overview.ja.md +30 -0
  92. package/docs/overview.zh-TW.md +30 -0
  93. package/docs/overview.zh.md +8 -8
  94. package/package.json +19 -11
  95. package/prompts/common/document/content-rules-core.md +19 -0
  96. package/prompts/common/document/media-handling-rules.md +9 -0
  97. package/prompts/common/document/role-and-personality.md +15 -0
  98. package/prompts/common/document/user-preferences.md +9 -0
  99. package/prompts/common/document-structure/conflict-resolution-guidance.md +16 -0
  100. package/prompts/common/document-structure/document-structure-rules.md +45 -0
  101. package/prompts/common/document-structure/glossary.md +7 -0
  102. package/prompts/common/document-structure/intj-traits.md +5 -0
  103. package/prompts/common/document-structure/output-constraints.md +9 -0
  104. package/prompts/common/document-structure/user-locale-rules.md +10 -0
  105. package/prompts/common/document-structure/user-preferences.md +9 -0
  106. package/prompts/detail/custom/custom-components.md +9 -1
  107. package/prompts/detail/document-rules.md +6 -6
  108. package/prompts/detail/generate-document.md +5 -45
  109. package/prompts/detail/update-document.md +145 -0
  110. package/prompts/evaluate/document-structure.md +94 -0
  111. package/prompts/evaluate/document.md +149 -0
  112. package/prompts/structure/document-rules.md +1 -1
  113. package/prompts/structure/generate-structure-system.md +74 -0
  114. package/prompts/structure/generate-structure-user.md +41 -0
  115. package/prompts/structure/update-document-structure.md +118 -0
  116. package/prompts/translate/translate-document.md +1 -1
  117. package/prompts/utils/feedback-refiner.md +3 -3
  118. package/release-please-config.json +1 -7
  119. package/tests/agents/clear/choose-contents.test.mjs +280 -0
  120. package/tests/agents/clear/clear-auth-tokens.test.mjs +268 -0
  121. package/tests/agents/clear/clear-document-config.test.mjs +167 -0
  122. package/tests/agents/clear/clear-document-structure.test.mjs +374 -0
  123. package/tests/agents/clear/clear-generated-docs.test.mjs +222 -0
  124. package/tests/agents/evaluate/code-snippet.test.mjs +163 -0
  125. package/tests/agents/evaluate/fixtures/api-services.md +87 -0
  126. package/tests/agents/evaluate/fixtures/js-sdk.md +94 -0
  127. package/tests/agents/evaluate/generate-report.test.mjs +312 -0
  128. package/tests/agents/generate/check-document-structure.test.mjs +0 -6
  129. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +449 -0
  130. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +410 -0
  131. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +476 -0
  132. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +548 -0
  133. package/tests/agents/generate/generate-structure.test.mjs +0 -6
  134. package/tests/agents/generate/user-review-document-structure.test.mjs +9 -9
  135. package/tests/agents/publish/publish-docs.test.mjs +2 -2
  136. package/tests/agents/update/check-update-is-single.test.mjs +300 -0
  137. package/tests/agents/update/document-tools/update-document-content.test.mjs +326 -0
  138. package/tests/agents/update/user-review-document.test.mjs +561 -0
  139. package/tests/agents/utils/format-document-structure.test.mjs +100 -0
  140. package/tests/utils/auth-utils.test.mjs +239 -1
  141. package/tests/utils/blocklet.test.mjs +9 -7
  142. package/tests/utils/constants.test.mjs +1 -1
  143. package/tests/utils/d2-utils.test.mjs +1 -1
  144. package/tests/utils/deploy.test.mjs +310 -366
  145. package/tests/utils/kroki-utils.test.mjs +2 -15
  146. package/tests/utils/linter/fixtures/css/keyword-error.css +1 -0
  147. package/tests/utils/linter/fixtures/css/missing-semicolon.css +1 -0
  148. package/tests/utils/linter/fixtures/css/syntax-error.css +1 -0
  149. package/tests/utils/linter/fixtures/css/undeclare-variable.css +1 -0
  150. package/tests/utils/linter/fixtures/css/unused-variable.css +2 -0
  151. package/tests/utils/linter/fixtures/css/valid-code.css +1 -0
  152. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +1 -0
  153. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +2 -0
  154. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +2 -0
  155. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +1 -0
  156. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +1 -0
  157. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +2 -0
  158. package/tests/utils/linter/fixtures/go/keyword-error.go +5 -0
  159. package/tests/utils/linter/fixtures/go/missing-semicolon.go +5 -0
  160. package/tests/utils/linter/fixtures/go/syntax-error.go +6 -0
  161. package/tests/utils/linter/fixtures/go/undeclare-variable.go +5 -0
  162. package/tests/utils/linter/fixtures/go/unused-variable.go +5 -0
  163. package/tests/utils/linter/fixtures/go/valid-code.go +7 -0
  164. package/tests/utils/linter/fixtures/js/keyword-error.js +3 -0
  165. package/tests/utils/linter/fixtures/js/missing-semicolon.js +6 -0
  166. package/tests/utils/linter/fixtures/js/syntax-error.js +4 -0
  167. package/tests/utils/linter/fixtures/js/undeclare-variable.js +3 -0
  168. package/tests/utils/linter/fixtures/js/unused-variable.js +7 -0
  169. package/tests/utils/linter/fixtures/js/valid-code.js +15 -0
  170. package/tests/utils/linter/fixtures/json/keyword-error.json +1 -0
  171. package/tests/utils/linter/fixtures/json/missing-semicolon.json +1 -0
  172. package/tests/utils/linter/fixtures/json/syntax-error.json +1 -0
  173. package/tests/utils/linter/fixtures/json/undeclare-variable.json +1 -0
  174. package/tests/utils/linter/fixtures/json/unused-variable.json +1 -0
  175. package/tests/utils/linter/fixtures/json/valid-code.json +1 -0
  176. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +5 -0
  177. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +5 -0
  178. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +5 -0
  179. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +5 -0
  180. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +4 -0
  181. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +5 -0
  182. package/tests/utils/linter/fixtures/python/keyword-error.py +3 -0
  183. package/tests/utils/linter/fixtures/python/missing-semicolon.py +2 -0
  184. package/tests/utils/linter/fixtures/python/syntax-error.py +3 -0
  185. package/tests/utils/linter/fixtures/python/undeclare-variable.py +3 -0
  186. package/tests/utils/linter/fixtures/python/unused-variable.py +6 -0
  187. package/tests/utils/linter/fixtures/python/valid-code.py +12 -0
  188. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +2 -0
  189. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +1 -0
  190. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +2 -0
  191. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +1 -0
  192. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +2 -0
  193. package/tests/utils/linter/fixtures/ruby/valid-code.rb +1 -0
  194. package/tests/utils/linter/fixtures/sass/keyword-error.sass +2 -0
  195. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +3 -0
  196. package/tests/utils/linter/fixtures/sass/syntax-error.sass +3 -0
  197. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +2 -0
  198. package/tests/utils/linter/fixtures/sass/unused-variable.sass +4 -0
  199. package/tests/utils/linter/fixtures/sass/valid-code.sass +2 -0
  200. package/tests/utils/linter/fixtures/scss/keyword-error.scss +1 -0
  201. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +1 -0
  202. package/tests/utils/linter/fixtures/scss/syntax-error.scss +1 -0
  203. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +1 -0
  204. package/tests/utils/linter/fixtures/scss/unused-variable.scss +2 -0
  205. package/tests/utils/linter/fixtures/scss/valid-code.scss +1 -0
  206. package/tests/utils/linter/fixtures/shell/keyword-error.sh +5 -0
  207. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +3 -0
  208. package/tests/utils/linter/fixtures/shell/syntax-error.sh +4 -0
  209. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +3 -0
  210. package/tests/utils/linter/fixtures/shell/unused-variable.sh +4 -0
  211. package/tests/utils/linter/fixtures/shell/valid-code.sh +3 -0
  212. package/tests/utils/linter/fixtures/ts/keyword-error.ts +1 -0
  213. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +1 -0
  214. package/tests/utils/linter/fixtures/ts/syntax-error.ts +1 -0
  215. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +1 -0
  216. package/tests/utils/linter/fixtures/ts/unused-variable.ts +3 -0
  217. package/tests/utils/linter/fixtures/ts/valid-code.ts +3 -0
  218. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +5 -0
  219. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +5 -0
  220. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +5 -0
  221. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +6 -0
  222. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +6 -0
  223. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +5 -0
  224. package/tests/utils/linter/fixtures/vue/keyword-error.vue +6 -0
  225. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +6 -0
  226. package/tests/utils/linter/fixtures/vue/syntax-error.vue +6 -0
  227. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +6 -0
  228. package/tests/utils/linter/fixtures/vue/unused-variable.vue +7 -0
  229. package/tests/utils/linter/fixtures/vue/valid-code.vue +6 -0
  230. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +1 -0
  231. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +2 -0
  232. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +1 -0
  233. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +1 -0
  234. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +2 -0
  235. package/tests/utils/linter/fixtures/yaml/valid-code.yml +3 -0
  236. package/tests/utils/linter/index.test.mjs +440 -0
  237. package/tests/utils/linter/scan-results.mjs +42 -0
  238. package/tests/utils/markdown/index.test.mjs +478 -0
  239. package/tests/utils/mermaid-validator.test.mjs +2 -2
  240. package/tests/utils/utils.test.mjs +3 -1
  241. package/types/document-schema.mjs +54 -0
  242. package/types/document-structure-schema.mjs +244 -0
  243. package/utils/auth-utils.mjs +131 -6
  244. package/utils/conflict-detector.mjs +5 -1
  245. package/utils/{constants.mjs → constants/index.mjs} +109 -0
  246. package/utils/constants/linter.mjs +102 -0
  247. package/utils/d2-utils.mjs +2 -4
  248. package/utils/debug.mjs +3 -0
  249. package/utils/deploy.mjs +81 -385
  250. package/utils/evaluate/report-utils.mjs +131 -0
  251. package/utils/file-utils.mjs +36 -1
  252. package/utils/kroki-utils.mjs +1 -1
  253. package/utils/linter/index.mjs +50 -0
  254. package/utils/markdown/index.mjs +26 -0
  255. package/utils/markdown-checker.mjs +1 -1
  256. package/utils/utils.mjs +19 -7
  257. 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>