@aigne/doc-smith 0.8.12-beta.7 → 0.8.12-beta.9

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 (284) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/agents/clear/choose-contents.mjs +14 -1
  3. package/agents/clear/clear-media-description.mjs +129 -0
  4. package/agents/clear/index.yaml +3 -1
  5. package/agents/evaluate/code-snippet.mjs +28 -24
  6. package/agents/evaluate/document-structure.yaml +0 -4
  7. package/agents/evaluate/document.yaml +1 -5
  8. package/agents/generate/index.yaml +1 -0
  9. package/agents/init/index.mjs +10 -0
  10. package/agents/media/batch-generate-media-description.yaml +44 -0
  11. package/agents/media/generate-media-description.yaml +47 -0
  12. package/agents/media/load-media-description.mjs +238 -0
  13. package/agents/publish/index.yaml +4 -0
  14. package/agents/publish/publish-docs.mjs +77 -5
  15. package/agents/publish/translate-meta.mjs +103 -0
  16. package/agents/update/generate-document.yaml +30 -28
  17. package/agents/update/index.yaml +1 -0
  18. package/agents/update/update-document-detail.yaml +3 -1
  19. package/agents/utils/load-sources.mjs +103 -53
  20. package/agents/utils/update-branding.mjs +69 -0
  21. package/aigne.yaml +6 -0
  22. package/assets/report-template/report.html +34 -34
  23. package/package.json +17 -2
  24. package/prompts/common/document/role-and-personality.md +3 -1
  25. package/prompts/detail/d2-diagram/guide.md +7 -1
  26. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  27. package/prompts/detail/generate/system-prompt.md +6 -7
  28. package/prompts/detail/generate/user-prompt.md +12 -3
  29. package/prompts/detail/update/user-prompt.md +0 -2
  30. package/prompts/evaluate/document-structure.md +6 -7
  31. package/prompts/evaluate/document.md +16 -25
  32. package/prompts/media/media-description/system-prompt.md +35 -0
  33. package/prompts/media/media-description/user-prompt.md +8 -0
  34. package/prompts/structure/update/user-prompt.md +0 -4
  35. package/utils/constants/index.mjs +0 -107
  36. package/utils/file-utils.mjs +86 -0
  37. package/utils/markdown-checker.mjs +0 -20
  38. package/utils/request.mjs +7 -0
  39. package/utils/upload-files.mjs +231 -0
  40. package/utils/utils.mjs +11 -1
  41. package/.aigne/doc-smith/config.yaml +0 -77
  42. package/.aigne/doc-smith/history.yaml +0 -37
  43. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  44. package/.aigne/doc-smith/preferences.yml +0 -97
  45. package/.aigne/doc-smith/upload-cache.yaml +0 -1893
  46. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  47. package/.github/workflows/ci.yml +0 -54
  48. package/.github/workflows/create-release-pr.yaml +0 -21
  49. package/.github/workflows/publish-docs.yml +0 -65
  50. package/.github/workflows/release.yml +0 -49
  51. package/.github/workflows/reviewer.yml +0 -54
  52. package/.release-please-manifest.json +0 -3
  53. package/RELEASE.md +0 -9
  54. package/assets/screenshots/doc-complete-setup.png +0 -0
  55. package/assets/screenshots/doc-generate-docs.png +0 -0
  56. package/assets/screenshots/doc-generate.png +0 -0
  57. package/assets/screenshots/doc-generated-successfully.png +0 -0
  58. package/assets/screenshots/doc-publish.png +0 -0
  59. package/assets/screenshots/doc-regenerate.png +0 -0
  60. package/assets/screenshots/doc-translate-langs.png +0 -0
  61. package/assets/screenshots/doc-translate.png +0 -0
  62. package/assets/screenshots/doc-update.png +0 -0
  63. package/biome.json +0 -73
  64. package/codecov.yml +0 -15
  65. package/docs/_sidebar.md +0 -15
  66. package/docs/configuration-initial-setup.ja.md +0 -179
  67. package/docs/configuration-initial-setup.md +0 -179
  68. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  69. package/docs/configuration-initial-setup.zh.md +0 -179
  70. package/docs/configuration-managing-preferences.ja.md +0 -100
  71. package/docs/configuration-managing-preferences.md +0 -100
  72. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  73. package/docs/configuration-managing-preferences.zh.md +0 -100
  74. package/docs/configuration.ja.md +0 -96
  75. package/docs/configuration.md +0 -96
  76. package/docs/configuration.zh-TW.md +0 -96
  77. package/docs/configuration.zh.md +0 -96
  78. package/docs/getting-started.ja.md +0 -88
  79. package/docs/getting-started.md +0 -88
  80. package/docs/getting-started.zh-TW.md +0 -88
  81. package/docs/getting-started.zh.md +0 -88
  82. package/docs/guides-cleaning-up.ja.md +0 -51
  83. package/docs/guides-cleaning-up.md +0 -51
  84. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  85. package/docs/guides-cleaning-up.zh.md +0 -51
  86. package/docs/guides-evaluating-documents.ja.md +0 -66
  87. package/docs/guides-evaluating-documents.md +0 -66
  88. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  89. package/docs/guides-evaluating-documents.zh.md +0 -66
  90. package/docs/guides-generating-documentation.ja.md +0 -151
  91. package/docs/guides-generating-documentation.md +0 -151
  92. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  93. package/docs/guides-generating-documentation.zh.md +0 -151
  94. package/docs/guides-interactive-chat.ja.md +0 -85
  95. package/docs/guides-interactive-chat.md +0 -85
  96. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  97. package/docs/guides-interactive-chat.zh.md +0 -85
  98. package/docs/guides-managing-history.ja.md +0 -48
  99. package/docs/guides-managing-history.md +0 -48
  100. package/docs/guides-managing-history.zh-TW.md +0 -48
  101. package/docs/guides-managing-history.zh.md +0 -48
  102. package/docs/guides-publishing-your-docs.ja.md +0 -78
  103. package/docs/guides-publishing-your-docs.md +0 -78
  104. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  105. package/docs/guides-publishing-your-docs.zh.md +0 -78
  106. package/docs/guides-translating-documentation.ja.md +0 -95
  107. package/docs/guides-translating-documentation.md +0 -95
  108. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  109. package/docs/guides-translating-documentation.zh.md +0 -95
  110. package/docs/guides-updating-documentation.ja.md +0 -77
  111. package/docs/guides-updating-documentation.md +0 -77
  112. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  113. package/docs/guides-updating-documentation.zh.md +0 -77
  114. package/docs/guides.ja.md +0 -32
  115. package/docs/guides.md +0 -32
  116. package/docs/guides.zh-TW.md +0 -32
  117. package/docs/guides.zh.md +0 -32
  118. package/docs/overview.ja.md +0 -61
  119. package/docs/overview.md +0 -61
  120. package/docs/overview.zh-TW.md +0 -61
  121. package/docs/overview.zh.md +0 -61
  122. package/docs/release-notes.ja.md +0 -255
  123. package/docs/release-notes.md +0 -255
  124. package/docs/release-notes.zh-TW.md +0 -255
  125. package/docs/release-notes.zh.md +0 -255
  126. package/media.md +0 -19
  127. package/prompts/common/afs/afs-tools-usage.md +0 -5
  128. package/prompts/common/afs/use-afs-instruction.md +0 -1
  129. package/release-please-config.json +0 -14
  130. package/tests/agents/chat/chat.test.mjs +0 -46
  131. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  132. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  133. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  134. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  135. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  136. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  137. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  138. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  139. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  140. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  141. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  142. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  143. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  144. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  145. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  146. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  147. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  148. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  149. package/tests/agents/history/view.test.mjs +0 -97
  150. package/tests/agents/init/init.test.mjs +0 -1657
  151. package/tests/agents/prefs/prefs.test.mjs +0 -431
  152. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  153. package/tests/agents/translate/choose-language.test.mjs +0 -311
  154. package/tests/agents/translate/translate-document.test.mjs +0 -51
  155. package/tests/agents/update/check-document.test.mjs +0 -463
  156. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  157. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  158. package/tests/agents/update/generate-document.test.mjs +0 -51
  159. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  160. package/tests/agents/update/user-review-document.test.mjs +0 -582
  161. package/tests/agents/utils/action-success.test.mjs +0 -54
  162. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  163. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  164. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  165. package/tests/agents/utils/exit.test.mjs +0 -70
  166. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  167. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  168. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  169. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  170. package/tests/agents/utils/fs.test.mjs +0 -267
  171. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  172. package/tests/agents/utils/save-docs.test.mjs +0 -109
  173. package/tests/agents/utils/save-output.test.mjs +0 -315
  174. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  175. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  176. package/tests/utils/auth-utils.test.mjs +0 -596
  177. package/tests/utils/blocklet.test.mjs +0 -336
  178. package/tests/utils/conflict-detector.test.mjs +0 -355
  179. package/tests/utils/constants.test.mjs +0 -295
  180. package/tests/utils/d2-utils.test.mjs +0 -437
  181. package/tests/utils/deploy.test.mjs +0 -399
  182. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  183. package/tests/utils/file-utils.test.mjs +0 -521
  184. package/tests/utils/history-utils.test.mjs +0 -206
  185. package/tests/utils/kroki-utils.test.mjs +0 -646
  186. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  187. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  188. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  189. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  190. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  191. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  192. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  193. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  194. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  195. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  196. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  197. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  198. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  199. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  200. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  201. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  202. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  203. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  204. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  205. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  206. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  207. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  208. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  209. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  210. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  211. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  212. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  213. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  214. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  215. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  216. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  217. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  218. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  219. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  220. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  221. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  222. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  223. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  224. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  225. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  226. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  227. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  228. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  229. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  230. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  231. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  232. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  233. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  234. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  235. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  236. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  237. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  238. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  239. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  240. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  241. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  242. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  243. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  244. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  245. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  246. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  247. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  248. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  249. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  250. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  251. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  252. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  253. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  254. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  255. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  256. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  257. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  258. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  259. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  260. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  261. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  262. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  263. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  264. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  265. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  266. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  267. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  268. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  269. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  270. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  271. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  272. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  273. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  274. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  275. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  276. package/tests/utils/linter/index.test.mjs +0 -440
  277. package/tests/utils/linter/scan-results.mjs +0 -42
  278. package/tests/utils/load-config.test.mjs +0 -141
  279. package/tests/utils/markdown/index.test.mjs +0 -478
  280. package/tests/utils/mermaid-validator.test.mjs +0 -541
  281. package/tests/utils/mock-chat-model.mjs +0 -12
  282. package/tests/utils/preferences-utils.test.mjs +0 -465
  283. package/tests/utils/save-value-to-config.test.mjs +0 -483
  284. package/tests/utils/utils.test.mjs +0 -941
@@ -1,596 +0,0 @@
1
- import {
2
- afterAll,
3
- afterEach,
4
- beforeAll,
5
- beforeEach,
6
- describe,
7
- expect,
8
- mock,
9
- spyOn,
10
- test,
11
- } from "bun:test";
12
- import * as fs from "node:fs";
13
- import * as fsPromises from "node:fs/promises";
14
- import * as os from "node:os";
15
- import * as path from "node:path";
16
- import * as yaml from "yaml";
17
- import { getAccessToken, getOfficialAccessToken } from "../../utils/auth-utils.mjs";
18
- import * as blockletUtils from "../../utils/blocklet.mjs";
19
- import { DOC_OFFICIAL_ACCESS_TOKEN } from "../../utils/constants/index.mjs";
20
-
21
- // Mock external modules that involve network requests
22
- const mockCreateConnect = mock(() => Promise.resolve({ accessKeySecret: "new-access-token" }));
23
- const mockOpen = mock(() => Promise.resolve());
24
- const mockJoinURL = mock((base, path) => `${base}${path}`);
25
-
26
- describe("auth-utils", () => {
27
- let originalEnv;
28
-
29
- // Spies for internal operations
30
- let existsSyncSpy;
31
- let readFileSpy;
32
- let writeFileSpy;
33
- let mkdirSyncSpy;
34
- let homedirSpy;
35
- let joinSpy;
36
- let parseSpy;
37
- let stringifySpy;
38
- let getComponentMountPointSpy;
39
- let consoleWarnSpy;
40
- let consoleDebugSpy;
41
-
42
- beforeAll(() => {
43
- // Apply mocks for external dependencies that involve network requests
44
- mock.module("@aigne/cli/utils/aigne-hub/credential.js", () => ({
45
- createConnect: mockCreateConnect,
46
- }));
47
- mock.module("open", () => ({ default: mockOpen }));
48
- mock.module("join-url", () => ({ default: mockJoinURL }));
49
- });
50
-
51
- afterAll(() => {
52
- // Restore all mocks when this test file is complete
53
- mock.restore();
54
- });
55
-
56
- beforeEach(() => {
57
- originalEnv = { ...process.env };
58
- delete process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN;
59
- delete process.env[DOC_OFFICIAL_ACCESS_TOKEN];
60
-
61
- // Reset external mocks
62
- mockCreateConnect.mockClear();
63
- mockCreateConnect.mockImplementation(() =>
64
- Promise.resolve({ accessKeySecret: "new-access-token" }),
65
- );
66
- mockOpen.mockClear();
67
- mockOpen.mockImplementation(() => Promise.resolve());
68
- mockJoinURL.mockClear();
69
- mockJoinURL.mockImplementation((base, path) => `${base}${path}`);
70
-
71
- // Spy on filesystem operations
72
- existsSyncSpy = spyOn(fs, "existsSync").mockReturnValue(false);
73
- readFileSpy = spyOn(fsPromises, "readFile").mockResolvedValue("");
74
- writeFileSpy = spyOn(fsPromises, "writeFile").mockResolvedValue();
75
- mkdirSyncSpy = spyOn(fs, "mkdirSync").mockImplementation(() => {});
76
-
77
- // Spy on path operations
78
- homedirSpy = spyOn(os, "homedir").mockReturnValue("/mock/home");
79
- joinSpy = spyOn(path, "join").mockImplementation((...paths) => paths.join("/"));
80
-
81
- // Spy on YAML operations
82
- parseSpy = spyOn(yaml, "parse").mockReturnValue({});
83
- stringifySpy = spyOn(yaml, "stringify").mockReturnValue("mock yaml");
84
-
85
- // Spy on blocklet operations
86
- getComponentMountPointSpy = spyOn(blockletUtils, "getComponentMountPoint").mockResolvedValue();
87
-
88
- // Spy on console methods
89
- consoleWarnSpy = spyOn(console, "warn").mockImplementation(() => {});
90
- consoleDebugSpy = spyOn(console, "debug").mockImplementation(() => {});
91
- });
92
-
93
- afterEach(() => {
94
- process.env = originalEnv;
95
-
96
- // Restore all spies
97
- existsSyncSpy?.mockRestore();
98
- readFileSpy?.mockRestore();
99
- writeFileSpy?.mockRestore();
100
- mkdirSyncSpy?.mockRestore();
101
- homedirSpy?.mockRestore();
102
- joinSpy?.mockRestore();
103
- parseSpy?.mockRestore();
104
- stringifySpy?.mockRestore();
105
- getComponentMountPointSpy?.mockRestore();
106
- consoleWarnSpy?.mockRestore();
107
- consoleDebugSpy?.mockRestore();
108
- });
109
-
110
- test("should return access token from environment variable", async () => {
111
- process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = "env-token";
112
-
113
- const result = await getAccessToken("https://example.com");
114
-
115
- expect(result).toBe("env-token");
116
- });
117
-
118
- test("should handle different URL formats", async () => {
119
- process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = "test-token";
120
-
121
- // Test different URL formats
122
- const urls = [
123
- "https://example.com",
124
- "http://example.com",
125
- "https://example.com:8080",
126
- "https://sub.example.com/path",
127
- ];
128
-
129
- for (const url of urls) {
130
- const result = await getAccessToken(url);
131
- expect(result).toBe("test-token");
132
- }
133
- });
134
-
135
- test("should handle invalid URL", async () => {
136
- process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = "test-token";
137
-
138
- await expect(getAccessToken("invalid-url")).rejects.toThrow();
139
- });
140
-
141
- test("should work with localhost URLs", async () => {
142
- process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = "local-token";
143
-
144
- const result = await getAccessToken("http://localhost:3000");
145
-
146
- expect(result).toBe("local-token");
147
- });
148
-
149
- test("should preserve environment variable after function call", async () => {
150
- process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = "persistent-token";
151
-
152
- await getAccessToken("https://example.com");
153
-
154
- expect(process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN).toBe("persistent-token");
155
- });
156
-
157
- // CONFIG FILE READING TESTS
158
- test("should read access token from config file", async () => {
159
- existsSyncSpy.mockReturnValue(true);
160
- readFileSpy.mockResolvedValue("DOC_DISCUSS_KIT_ACCESS_TOKEN: config-token");
161
- parseSpy.mockReturnValue({
162
- "example.com": {
163
- DOC_DISCUSS_KIT_ACCESS_TOKEN: "config-token",
164
- },
165
- });
166
-
167
- const result = await getAccessToken("https://example.com");
168
-
169
- expect(result).toBe("config-token");
170
- expect(joinSpy).toHaveBeenCalledWith("/mock/home", ".aigne", "doc-smith-connected.yaml");
171
- expect(existsSyncSpy).toHaveBeenCalledWith("/mock/home/.aigne/doc-smith-connected.yaml");
172
- expect(readFileSpy).toHaveBeenCalledWith("/mock/home/.aigne/doc-smith-connected.yaml", "utf8");
173
- expect(parseSpy).toHaveBeenCalled();
174
- });
175
-
176
- test("should handle config file without token field", async () => {
177
- existsSyncSpy.mockReturnValue(true);
178
- readFileSpy.mockResolvedValue("other: value");
179
- parseSpy.mockReturnValue({
180
- "example.com": {
181
- other: "value",
182
- },
183
- });
184
-
185
- // Since we now have successful authorization flow mocked, this should succeed
186
- const result = await getAccessToken("https://example.com");
187
- expect(result).toBe("new-access-token");
188
- expect(getComponentMountPointSpy).toHaveBeenCalled();
189
- });
190
-
191
- test("should handle missing hostname in config", async () => {
192
- existsSyncSpy.mockReturnValue(true);
193
- readFileSpy.mockResolvedValue("DOC_DISCUSS_KIT_ACCESS_TOKEN: token");
194
- parseSpy.mockReturnValue({
195
- "other-domain.com": {
196
- DOC_DISCUSS_KIT_ACCESS_TOKEN: "other-token",
197
- },
198
- });
199
-
200
- // Since we now have successful authorization flow mocked, this should succeed
201
- const result = await getAccessToken("https://example.com");
202
- expect(result).toBe("new-access-token");
203
- expect(getComponentMountPointSpy).toHaveBeenCalled();
204
- });
205
-
206
- test("should handle config file read errors", async () => {
207
- existsSyncSpy.mockReturnValue(true);
208
- readFileSpy.mockRejectedValue(new Error("File read error"));
209
- // Make createConnect fail to test the error path
210
- mockCreateConnect.mockRejectedValueOnce(new Error("Network error"));
211
-
212
- await expect(getAccessToken("https://example.com")).rejects.toThrow(
213
- "Failed to obtain access token. Please check your network connection and try again later.",
214
- );
215
- expect(consoleWarnSpy).toHaveBeenCalledWith("Failed to read config file:", "File read error");
216
- });
217
-
218
- test("should handle config file without DOC_DISCUSS_KIT_ACCESS_TOKEN keyword", async () => {
219
- existsSyncSpy.mockReturnValue(true);
220
- readFileSpy.mockResolvedValue("some other content");
221
-
222
- // Should succeed with authorization flow
223
- const result = await getAccessToken("https://example.com");
224
- expect(result).toBe("new-access-token");
225
- // Verify that the config file was read but the flow proceeded to authorization
226
- expect(readFileSpy).toHaveBeenCalled();
227
- });
228
-
229
- // ERROR HANDLING TESTS
230
- test("should throw error for invalid blocklet", async () => {
231
- const InvalidBlockletError = (await import("../../utils/blocklet.mjs")).InvalidBlockletError;
232
- getComponentMountPointSpy.mockRejectedValue(new InvalidBlockletError());
233
-
234
- await expect(getAccessToken("https://example.com")).rejects.toThrow(
235
- "The provided URL is not a valid website on ArcBlock platform",
236
- );
237
- });
238
-
239
- test("should throw error for missing component", async () => {
240
- const ComponentNotFoundError = (await import("../../utils/blocklet.mjs"))
241
- .ComponentNotFoundError;
242
- getComponentMountPointSpy.mockRejectedValue(new ComponentNotFoundError());
243
-
244
- await expect(getAccessToken("https://example.com")).rejects.toThrow(
245
- "This website does not have required components for publishing",
246
- );
247
- });
248
-
249
- test("should throw error for network issues", async () => {
250
- getComponentMountPointSpy.mockRejectedValue(new Error("Network error"));
251
-
252
- await expect(getAccessToken("https://example.com")).rejects.toThrow("Unable to connect to:");
253
- });
254
-
255
- // AUTHORIZATION FLOW TESTS
256
- test("should successfully complete authorization flow", async () => {
257
- // Mock successful component check
258
- getComponentMountPointSpy.mockResolvedValue({ endpoint: "https://example.com/api" });
259
-
260
- const result = await getAccessToken("https://example.com");
261
-
262
- expect(result).toBe("new-access-token");
263
-
264
- // Verify the authorization flow
265
- expect(getComponentMountPointSpy).toHaveBeenCalledWith(
266
- "https://example.com",
267
- expect.any(String),
268
- );
269
- expect(mockCreateConnect).toHaveBeenCalledWith(
270
- expect.objectContaining({
271
- connectAction: "gen-simple-access-key",
272
- source: "AIGNE DocSmith connect to website",
273
- closeOnSuccess: true,
274
- appName: "AIGNE DocSmith",
275
- openPage: expect.any(Function),
276
- }),
277
- );
278
-
279
- // Verify environment variable is set
280
- expect(process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN).toBe("new-access-token");
281
-
282
- // Verify config file is saved
283
- expect(writeFileSpy).toHaveBeenCalledWith(
284
- "/mock/home/.aigne/doc-smith-connected.yaml",
285
- "mock yaml",
286
- );
287
- expect(stringifySpy).toHaveBeenCalledWith(
288
- expect.objectContaining({
289
- "example.com": {
290
- DOC_DISCUSS_KIT_ACCESS_TOKEN: "new-access-token",
291
- DOC_DISCUSS_KIT_URL: "https://example.com",
292
- },
293
- }),
294
- );
295
- });
296
-
297
- test("should create .aigne directory if it doesn't exist", async () => {
298
- getComponentMountPointSpy.mockResolvedValue({ endpoint: "https://example.com/api" });
299
- existsSyncSpy.mockReturnValueOnce(false); // .aigne directory doesn't exist
300
-
301
- const result = await getAccessToken("https://example.com");
302
-
303
- expect(result).toBe("new-access-token");
304
- expect(mkdirSyncSpy).toHaveBeenCalledWith("/mock/home/.aigne", { recursive: true });
305
- });
306
-
307
- test("should merge with existing config file", async () => {
308
- getComponentMountPointSpy.mockResolvedValue({ endpoint: "https://example.com/api" });
309
- existsSyncSpy
310
- .mockReturnValueOnce(true) // .aigne directory exists
311
- .mockReturnValueOnce(true); // config file exists at save time
312
- readFileSpy.mockResolvedValue("other.com:\n token: other-token");
313
- parseSpy.mockReturnValue({ "other.com": { token: "other-token" } });
314
-
315
- const result = await getAccessToken("https://example.com");
316
-
317
- expect(result).toBe("new-access-token");
318
- expect(stringifySpy).toHaveBeenCalled();
319
- // Verify that config file writing was attempted
320
- expect(writeFileSpy).toHaveBeenCalled();
321
- });
322
-
323
- test("should call openPage function with correct URL", async () => {
324
- getComponentMountPointSpy.mockResolvedValue({ endpoint: "https://example.com/api" });
325
-
326
- let capturedOpenPage;
327
- mockCreateConnect.mockImplementation((options) => {
328
- capturedOpenPage = options.openPage;
329
- return Promise.resolve({ accessKeySecret: "new-access-token" });
330
- });
331
-
332
- const result = await getAccessToken("https://example.com");
333
-
334
- expect(result).toBe("new-access-token");
335
- expect(typeof capturedOpenPage).toBe("function");
336
-
337
- // Test that openPage calls the mock open function
338
- await capturedOpenPage("https://auth.example.com");
339
- expect(mockOpen).toHaveBeenCalledWith("https://auth.example.com/?required_roles=owner%2Cadmin");
340
- });
341
-
342
- test("should handle authorization failure", async () => {
343
- getComponentMountPointSpy.mockResolvedValue({ endpoint: "https://example.com/api" });
344
- mockCreateConnect.mockRejectedValue(new Error("Authorization failed"));
345
-
346
- await expect(getAccessToken("https://example.com")).rejects.toThrow(
347
- "Failed to obtain access token. Please check your network connection and try again later.",
348
- );
349
- expect(consoleDebugSpy).toHaveBeenCalledWith(expect.any(Error));
350
- });
351
-
352
- test("should handle createConnect with different error types", async () => {
353
- getComponentMountPointSpy.mockResolvedValue({ endpoint: "https://example.com/api" });
354
- mockCreateConnect.mockRejectedValue(new TypeError("Network error"));
355
-
356
- await expect(getAccessToken("https://example.com")).rejects.toThrow(
357
- "Failed to obtain access token. Please check your network connection and try again later.",
358
- );
359
- });
360
-
361
- // OFFICIAL ACCESS TOKEN TESTS
362
- describe("getOfficialAccessToken", () => {
363
- test("should throw error for missing baseUrl parameter", async () => {
364
- await expect(getOfficialAccessToken()).rejects.toThrow(
365
- "baseUrl parameter is required for getOfficialAccessToken.",
366
- );
367
- await expect(getOfficialAccessToken("")).rejects.toThrow(
368
- "baseUrl parameter is required for getOfficialAccessToken.",
369
- );
370
- });
371
-
372
- test("should return access token from environment variable", async () => {
373
- process.env[DOC_OFFICIAL_ACCESS_TOKEN] = "env-official-token";
374
-
375
- const result = await getOfficialAccessToken("https://example.com");
376
-
377
- expect(result).toBe("env-official-token");
378
- });
379
-
380
- test("should handle different URL formats", async () => {
381
- process.env[DOC_OFFICIAL_ACCESS_TOKEN] = "test-official-token";
382
-
383
- const urls = [
384
- "https://example.com",
385
- "http://example.com",
386
- "https://example.com:8080",
387
- "https://sub.example.com/path",
388
- ];
389
-
390
- for (const url of urls) {
391
- const result = await getOfficialAccessToken(url);
392
- expect(result).toBe("test-official-token");
393
- }
394
- });
395
-
396
- test("should handle invalid URL", async () => {
397
- process.env[DOC_OFFICIAL_ACCESS_TOKEN] = "test-official-token";
398
-
399
- await expect(getOfficialAccessToken("invalid-url")).rejects.toThrow();
400
- });
401
-
402
- test("should work with localhost URLs", async () => {
403
- process.env[DOC_OFFICIAL_ACCESS_TOKEN] = "local-official-token";
404
-
405
- const result = await getOfficialAccessToken("http://localhost:3000");
406
-
407
- expect(result).toBe("local-official-token");
408
- });
409
-
410
- test("should preserve environment variable after function call", async () => {
411
- process.env[DOC_OFFICIAL_ACCESS_TOKEN] = "persistent-official-token";
412
-
413
- await getOfficialAccessToken("https://example.com");
414
-
415
- expect(process.env[DOC_OFFICIAL_ACCESS_TOKEN]).toBe("persistent-official-token");
416
- });
417
-
418
- // CONFIG FILE READING TESTS
419
- test("should read access token from config file", async () => {
420
- existsSyncSpy.mockReturnValue(true);
421
- readFileSpy.mockResolvedValue(`DOC_OFFICIAL_ACCESS_TOKEN: config-official-token`);
422
- parseSpy.mockReturnValue({
423
- "example.com": {
424
- [DOC_OFFICIAL_ACCESS_TOKEN]: "config-official-token",
425
- },
426
- });
427
-
428
- const result = await getOfficialAccessToken("https://example.com");
429
-
430
- expect(result).toBe("config-official-token");
431
- expect(joinSpy).toHaveBeenCalledWith("/mock/home", ".aigne", "doc-smith-connected.yaml");
432
- expect(existsSyncSpy).toHaveBeenCalledWith("/mock/home/.aigne/doc-smith-connected.yaml");
433
- expect(readFileSpy).toHaveBeenCalledWith(
434
- "/mock/home/.aigne/doc-smith-connected.yaml",
435
- "utf8",
436
- );
437
- expect(parseSpy).toHaveBeenCalled();
438
- });
439
-
440
- test("should handle config file without token field", async () => {
441
- existsSyncSpy.mockReturnValue(true);
442
- readFileSpy.mockResolvedValue("other: value");
443
- parseSpy.mockReturnValue({
444
- "example.com": {
445
- other: "value",
446
- },
447
- });
448
-
449
- const result = await getOfficialAccessToken("https://example.com");
450
- expect(result).toBe("new-access-token");
451
- });
452
-
453
- test("should handle missing hostname in config", async () => {
454
- existsSyncSpy.mockReturnValue(true);
455
- readFileSpy.mockResolvedValue(`${DOC_OFFICIAL_ACCESS_TOKEN}: token`);
456
- parseSpy.mockReturnValue({
457
- "other-domain.com": {
458
- [DOC_OFFICIAL_ACCESS_TOKEN]: "other-token",
459
- },
460
- });
461
-
462
- const result = await getOfficialAccessToken("https://example.com");
463
- expect(result).toBe("new-access-token");
464
- });
465
-
466
- test("should handle config file read errors silently", async () => {
467
- existsSyncSpy.mockReturnValue(true);
468
- readFileSpy.mockRejectedValue(new Error("File read error"));
469
-
470
- const result = await getOfficialAccessToken("https://example.com");
471
- expect(result).toBe("new-access-token");
472
- // Should not call console.warn for config file errors in getOfficialAccessToken
473
- });
474
-
475
- test("should handle empty config file", async () => {
476
- existsSyncSpy.mockReturnValue(true);
477
- readFileSpy.mockResolvedValue("");
478
- parseSpy.mockReturnValue(null);
479
-
480
- const result = await getOfficialAccessToken("https://example.com");
481
- expect(result).toBe("new-access-token");
482
- });
483
-
484
- // AUTHORIZATION FLOW TESTS
485
- test("should successfully complete authorization flow", async () => {
486
- const result = await getOfficialAccessToken("https://example.com");
487
-
488
- expect(result).toBe("new-access-token");
489
-
490
- // Verify the authorization flow
491
- expect(mockCreateConnect).toHaveBeenCalledWith(
492
- expect.objectContaining({
493
- connectAction: "gen-simple-access-key",
494
- source: "AIGNE DocSmith connect to official service",
495
- closeOnSuccess: true,
496
- appName: "AIGNE DocSmith",
497
- openPage: expect.any(Function),
498
- }),
499
- );
500
-
501
- // Verify environment variable is set
502
- expect(process.env[DOC_OFFICIAL_ACCESS_TOKEN]).toBe("new-access-token");
503
-
504
- // Verify config file is saved
505
- expect(writeFileSpy).toHaveBeenCalledWith(
506
- "/mock/home/.aigne/doc-smith-connected.yaml",
507
- "mock yaml",
508
- );
509
- expect(stringifySpy).toHaveBeenCalledWith(
510
- expect.objectContaining({
511
- "example.com": {
512
- [DOC_OFFICIAL_ACCESS_TOKEN]: "new-access-token",
513
- },
514
- }),
515
- );
516
- });
517
-
518
- test("should create .aigne directory if it doesn't exist", async () => {
519
- existsSyncSpy.mockReturnValueOnce(false); // .aigne directory doesn't exist
520
-
521
- const result = await getOfficialAccessToken("https://example.com");
522
-
523
- expect(result).toBe("new-access-token");
524
- expect(mkdirSyncSpy).toHaveBeenCalledWith("/mock/home/.aigne", { recursive: true });
525
- });
526
-
527
- test("should merge with existing config file", async () => {
528
- existsSyncSpy
529
- .mockReturnValueOnce(true) // .aigne directory exists
530
- .mockReturnValueOnce(true); // config file exists at save time
531
- readFileSpy.mockResolvedValue("other.com:\n token: other-token");
532
- parseSpy.mockReturnValue({ "other.com": { token: "other-token" } });
533
-
534
- const result = await getOfficialAccessToken("https://example.com");
535
-
536
- expect(result).toBe("new-access-token");
537
- expect(stringifySpy).toHaveBeenCalled();
538
- expect(writeFileSpy).toHaveBeenCalled();
539
- });
540
-
541
- test("should call openPage function with correct behavior", async () => {
542
- let capturedOpenPage;
543
- mockCreateConnect.mockImplementation((options) => {
544
- capturedOpenPage = options.openPage;
545
- return Promise.resolve({ accessKeySecret: "new-access-token" });
546
- });
547
-
548
- const result = await getOfficialAccessToken("https://example.com");
549
-
550
- expect(result).toBe("new-access-token");
551
- expect(typeof capturedOpenPage).toBe("function");
552
-
553
- // Test that openPage calls the mock open function and logs
554
- const consoleSpy = spyOn(console, "log").mockImplementation(() => {});
555
- await capturedOpenPage("https://auth.example.com");
556
- expect(mockOpen).toHaveBeenCalledWith("https://auth.example.com");
557
- expect(consoleSpy).toHaveBeenCalledWith(
558
- "🔗 Please open this URL in your browser to authorize access: ",
559
- expect.any(String),
560
- "\n",
561
- );
562
- consoleSpy.mockRestore();
563
- });
564
-
565
- test("should handle authorization failure", async () => {
566
- mockCreateConnect.mockRejectedValue(new Error("Authorization failed"));
567
-
568
- await expect(getOfficialAccessToken("https://example.com")).rejects.toThrow(
569
- "Failed to obtain official access token. Please check your network connection and try again later.",
570
- );
571
- expect(consoleDebugSpy).toHaveBeenCalledWith(expect.any(Error));
572
- });
573
-
574
- test("should handle createConnect with different error types", async () => {
575
- mockCreateConnect.mockRejectedValue(new TypeError("Network error"));
576
-
577
- await expect(getOfficialAccessToken("https://example.com")).rejects.toThrow(
578
- "Failed to obtain official access token. Please check your network connection and try again later.",
579
- );
580
- });
581
-
582
- test("should handle config file save errors gracefully", async () => {
583
- writeFileSpy.mockRejectedValue(new Error("Write error"));
584
- const consoleWarnSpy = spyOn(console, "warn").mockImplementation(() => {});
585
-
586
- const result = await getOfficialAccessToken("https://example.com");
587
-
588
- expect(result).toBe("new-access-token");
589
- expect(consoleWarnSpy).toHaveBeenCalledWith(
590
- expect.stringContaining("Failed to save token to config file"),
591
- expect.any(Error),
592
- );
593
- consoleWarnSpy.mockRestore();
594
- });
595
- });
596
- });