offcourse 1.0.0 → 1.1.0

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 (280) hide show
  1. package/README.md +107 -8
  2. package/dist/cli/commands/config.js.map +1 -1
  3. package/dist/cli/commands/inspect.js +1 -1
  4. package/dist/cli/commands/inspect.js.map +1 -1
  5. package/dist/cli/commands/sync.d.ts +1 -2
  6. package/dist/cli/commands/sync.d.ts.map +1 -1
  7. package/dist/cli/commands/sync.js +17 -15
  8. package/dist/cli/commands/sync.js.map +1 -1
  9. package/dist/cli/commands/syncHighLevel.d.ts +1 -2
  10. package/dist/cli/commands/syncHighLevel.d.ts.map +1 -1
  11. package/dist/cli/commands/syncHighLevel.js +8 -9
  12. package/dist/cli/commands/syncHighLevel.js.map +1 -1
  13. package/dist/cli/commands/syncLearningSuite.d.ts +35 -0
  14. package/dist/cli/commands/syncLearningSuite.d.ts.map +1 -0
  15. package/dist/cli/commands/syncLearningSuite.js +765 -0
  16. package/dist/cli/commands/syncLearningSuite.js.map +1 -0
  17. package/dist/cli/index.js +39 -1
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/config/configManager.d.ts.map +1 -1
  20. package/dist/config/configManager.js +4 -0
  21. package/dist/config/configManager.js.map +1 -1
  22. package/dist/downloader/hlsDownloader.d.ts +10 -4
  23. package/dist/downloader/hlsDownloader.d.ts.map +1 -1
  24. package/dist/downloader/hlsDownloader.js +60 -29
  25. package/dist/downloader/hlsDownloader.js.map +1 -1
  26. package/dist/downloader/hlsValidator.d.ts.map +1 -1
  27. package/dist/downloader/hlsValidator.js +6 -2
  28. package/dist/downloader/hlsValidator.js.map +1 -1
  29. package/dist/downloader/index.d.ts +7 -0
  30. package/dist/downloader/index.d.ts.map +1 -1
  31. package/dist/downloader/index.js +9 -6
  32. package/dist/downloader/index.js.map +1 -1
  33. package/dist/downloader/loomDownloader.d.ts +1 -1
  34. package/dist/downloader/loomDownloader.d.ts.map +1 -1
  35. package/dist/downloader/loomDownloader.js +32 -27
  36. package/dist/downloader/loomDownloader.js.map +1 -1
  37. package/dist/downloader/queue.d.ts +4 -4
  38. package/dist/downloader/queue.d.ts.map +1 -1
  39. package/dist/downloader/queue.js.map +1 -1
  40. package/dist/downloader/vimeoDownloader.d.ts.map +1 -1
  41. package/dist/downloader/vimeoDownloader.js +7 -3
  42. package/dist/downloader/vimeoDownloader.js.map +1 -1
  43. package/dist/scraper/extractor.d.ts +4 -0
  44. package/dist/scraper/extractor.d.ts.map +1 -1
  45. package/dist/scraper/extractor.js +79 -79
  46. package/dist/scraper/extractor.js.map +1 -1
  47. package/dist/scraper/highlevel/extractor.d.ts +11 -19
  48. package/dist/scraper/highlevel/extractor.d.ts.map +1 -1
  49. package/dist/scraper/highlevel/extractor.js +72 -85
  50. package/dist/scraper/highlevel/extractor.js.map +1 -1
  51. package/dist/scraper/highlevel/navigator.d.ts +3 -10
  52. package/dist/scraper/highlevel/navigator.d.ts.map +1 -1
  53. package/dist/scraper/highlevel/navigator.js +140 -127
  54. package/dist/scraper/highlevel/navigator.js.map +1 -1
  55. package/dist/scraper/highlevel/schemas.d.ts +188 -0
  56. package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
  57. package/dist/scraper/highlevel/schemas.js +139 -0
  58. package/dist/scraper/highlevel/schemas.js.map +1 -0
  59. package/dist/scraper/learningsuite/extractor.d.ts +50 -0
  60. package/dist/scraper/learningsuite/extractor.d.ts.map +1 -0
  61. package/dist/scraper/learningsuite/extractor.js +429 -0
  62. package/dist/scraper/learningsuite/extractor.js.map +1 -0
  63. package/dist/scraper/learningsuite/index.d.ts +4 -0
  64. package/dist/scraper/learningsuite/index.d.ts.map +1 -0
  65. package/dist/scraper/{ghl → learningsuite}/index.js +1 -1
  66. package/dist/scraper/learningsuite/index.js.map +1 -0
  67. package/dist/scraper/learningsuite/navigator.d.ts +122 -0
  68. package/dist/scraper/learningsuite/navigator.d.ts.map +1 -0
  69. package/dist/scraper/learningsuite/navigator.js +736 -0
  70. package/dist/scraper/learningsuite/navigator.js.map +1 -0
  71. package/dist/scraper/learningsuite/schemas.d.ts +270 -0
  72. package/dist/scraper/learningsuite/schemas.d.ts.map +1 -0
  73. package/dist/scraper/learningsuite/schemas.js +147 -0
  74. package/dist/scraper/learningsuite/schemas.js.map +1 -0
  75. package/dist/scraper/navigator.d.ts +14 -11
  76. package/dist/scraper/navigator.d.ts.map +1 -1
  77. package/dist/scraper/navigator.js +61 -104
  78. package/dist/scraper/navigator.js.map +1 -1
  79. package/dist/scraper/schemas.d.ts +57 -0
  80. package/dist/scraper/schemas.d.ts.map +1 -0
  81. package/dist/scraper/schemas.js +135 -0
  82. package/dist/scraper/schemas.js.map +1 -0
  83. package/dist/scraper/videoInterceptor.d.ts +4 -0
  84. package/dist/scraper/videoInterceptor.d.ts.map +1 -1
  85. package/dist/scraper/videoInterceptor.js +66 -51
  86. package/dist/scraper/videoInterceptor.js.map +1 -1
  87. package/dist/shared/auth.d.ts +9 -9
  88. package/dist/shared/auth.d.ts.map +1 -1
  89. package/dist/shared/auth.js +24 -38
  90. package/dist/shared/auth.js.map +1 -1
  91. package/dist/shared/firebase.d.ts +60 -0
  92. package/dist/shared/firebase.d.ts.map +1 -0
  93. package/dist/shared/firebase.js +102 -0
  94. package/dist/shared/firebase.js.map +1 -0
  95. package/dist/shared/fs.d.ts.map +1 -1
  96. package/dist/shared/fs.js +4 -0
  97. package/dist/shared/fs.js.map +1 -1
  98. package/dist/shared/index.d.ts +3 -0
  99. package/dist/shared/index.d.ts.map +1 -1
  100. package/dist/shared/index.js +3 -0
  101. package/dist/shared/index.js.map +1 -1
  102. package/dist/shared/slug.d.ts +11 -0
  103. package/dist/shared/slug.d.ts.map +1 -0
  104. package/{src/shared/slug.ts → dist/shared/slug.js} +10 -11
  105. package/dist/shared/slug.js.map +1 -0
  106. package/dist/shared/url.d.ts +43 -0
  107. package/dist/shared/url.d.ts.map +1 -0
  108. package/{src/shared/url.ts → dist/shared/url.js} +12 -15
  109. package/dist/shared/url.js.map +1 -0
  110. package/dist/state/database.d.ts +1 -0
  111. package/dist/state/database.d.ts.map +1 -1
  112. package/dist/state/database.js +3 -0
  113. package/dist/state/database.js.map +1 -1
  114. package/dist/storage/fileSystem.d.ts +17 -17
  115. package/dist/storage/fileSystem.d.ts.map +1 -1
  116. package/dist/storage/fileSystem.js +39 -31
  117. package/dist/storage/fileSystem.js.map +1 -1
  118. package/package.json +5 -2
  119. package/.github/workflows/ci.yml +0 -50
  120. package/.husky/commit-msg +0 -2
  121. package/.husky/pre-commit +0 -1
  122. package/.husky/pre-push +0 -3
  123. package/.prettierrc +0 -8
  124. package/.release-it.json +0 -23
  125. package/ARCHITECTURE.md +0 -233
  126. package/CHANGELOG.md +0 -78
  127. package/commitlint.config.js +0 -4
  128. package/dist/ai/openRouter.d.ts +0 -47
  129. package/dist/ai/openRouter.d.ts.map +0 -1
  130. package/dist/ai/openRouter.js +0 -116
  131. package/dist/ai/openRouter.js.map +0 -1
  132. package/dist/ai/transcriptPolisher.d.ts +0 -24
  133. package/dist/ai/transcriptPolisher.d.ts.map +0 -1
  134. package/dist/ai/transcriptPolisher.js +0 -89
  135. package/dist/ai/transcriptPolisher.js.map +0 -1
  136. package/dist/cli/commands/enrich.d.ts +0 -14
  137. package/dist/cli/commands/enrich.d.ts.map +0 -1
  138. package/dist/cli/commands/enrich.js +0 -271
  139. package/dist/cli/commands/enrich.js.map +0 -1
  140. package/dist/cli/commands/syncGhl.d.ts +0 -20
  141. package/dist/cli/commands/syncGhl.d.ts.map +0 -1
  142. package/dist/cli/commands/syncGhl.js +0 -483
  143. package/dist/cli/commands/syncGhl.js.map +0 -1
  144. package/dist/cli/commands/syncHighLevel.test.d.ts +0 -2
  145. package/dist/cli/commands/syncHighLevel.test.d.ts.map +0 -1
  146. package/dist/cli/commands/syncHighLevel.test.js +0 -102
  147. package/dist/cli/commands/syncHighLevel.test.js.map +0 -1
  148. package/dist/config/paths.test.d.ts +0 -2
  149. package/dist/config/paths.test.d.ts.map +0 -1
  150. package/dist/config/paths.test.js +0 -70
  151. package/dist/config/paths.test.js.map +0 -1
  152. package/dist/config/schema.test.d.ts +0 -2
  153. package/dist/config/schema.test.d.ts.map +0 -1
  154. package/dist/config/schema.test.js +0 -151
  155. package/dist/config/schema.test.js.map +0 -1
  156. package/dist/downloader/hlsDownloader.test.d.ts +0 -2
  157. package/dist/downloader/hlsDownloader.test.d.ts.map +0 -1
  158. package/dist/downloader/hlsDownloader.test.js +0 -116
  159. package/dist/downloader/hlsDownloader.test.js.map +0 -1
  160. package/dist/downloader/loomDownloader.test.d.ts +0 -2
  161. package/dist/downloader/loomDownloader.test.d.ts.map +0 -1
  162. package/dist/downloader/loomDownloader.test.js +0 -36
  163. package/dist/downloader/loomDownloader.test.js.map +0 -1
  164. package/dist/downloader/queue.test.d.ts +0 -2
  165. package/dist/downloader/queue.test.d.ts.map +0 -1
  166. package/dist/downloader/queue.test.js +0 -158
  167. package/dist/downloader/queue.test.js.map +0 -1
  168. package/dist/downloader/videoDownloader.d.ts +0 -32
  169. package/dist/downloader/videoDownloader.d.ts.map +0 -1
  170. package/dist/downloader/videoDownloader.js +0 -173
  171. package/dist/downloader/videoDownloader.js.map +0 -1
  172. package/dist/downloader/vimeoDownloader.test.d.ts +0 -2
  173. package/dist/downloader/vimeoDownloader.test.d.ts.map +0 -1
  174. package/dist/downloader/vimeoDownloader.test.js +0 -51
  175. package/dist/downloader/vimeoDownloader.test.js.map +0 -1
  176. package/dist/scraper/auth.d.ts +0 -29
  177. package/dist/scraper/auth.d.ts.map +0 -1
  178. package/dist/scraper/auth.js +0 -115
  179. package/dist/scraper/auth.js.map +0 -1
  180. package/dist/scraper/extractor.test.d.ts +0 -2
  181. package/dist/scraper/extractor.test.d.ts.map +0 -1
  182. package/dist/scraper/extractor.test.js +0 -65
  183. package/dist/scraper/extractor.test.js.map +0 -1
  184. package/dist/scraper/ghl/auth.d.ts +0 -25
  185. package/dist/scraper/ghl/auth.d.ts.map +0 -1
  186. package/dist/scraper/ghl/auth.js +0 -187
  187. package/dist/scraper/ghl/auth.js.map +0 -1
  188. package/dist/scraper/ghl/extractor.d.ts +0 -96
  189. package/dist/scraper/ghl/extractor.d.ts.map +0 -1
  190. package/dist/scraper/ghl/extractor.js +0 -345
  191. package/dist/scraper/ghl/extractor.js.map +0 -1
  192. package/dist/scraper/ghl/index.d.ts +0 -4
  193. package/dist/scraper/ghl/index.d.ts.map +0 -1
  194. package/dist/scraper/ghl/index.js.map +0 -1
  195. package/dist/scraper/ghl/navigator.d.ts +0 -93
  196. package/dist/scraper/ghl/navigator.d.ts.map +0 -1
  197. package/dist/scraper/ghl/navigator.js +0 -447
  198. package/dist/scraper/ghl/navigator.js.map +0 -1
  199. package/dist/scraper/highlevel/auth.d.ts +0 -25
  200. package/dist/scraper/highlevel/auth.d.ts.map +0 -1
  201. package/dist/scraper/highlevel/auth.js +0 -189
  202. package/dist/scraper/highlevel/auth.js.map +0 -1
  203. package/dist/scraper/highlevel/extractor.test.d.ts +0 -2
  204. package/dist/scraper/highlevel/extractor.test.d.ts.map +0 -1
  205. package/dist/scraper/highlevel/extractor.test.js +0 -101
  206. package/dist/scraper/highlevel/extractor.test.js.map +0 -1
  207. package/dist/scraper/highlevel/navigator.test.d.ts +0 -2
  208. package/dist/scraper/highlevel/navigator.test.d.ts.map +0 -1
  209. package/dist/scraper/highlevel/navigator.test.js +0 -78
  210. package/dist/scraper/highlevel/navigator.test.js.map +0 -1
  211. package/dist/scraper/navigator.test.d.ts +0 -2
  212. package/dist/scraper/navigator.test.d.ts.map +0 -1
  213. package/dist/scraper/navigator.test.js +0 -63
  214. package/dist/scraper/navigator.test.js.map +0 -1
  215. package/dist/scraper/skoolApi.d.ts +0 -17
  216. package/dist/scraper/skoolApi.d.ts.map +0 -1
  217. package/dist/scraper/skoolApi.js +0 -72
  218. package/dist/scraper/skoolApi.js.map +0 -1
  219. package/dist/state/database.test.d.ts +0 -2
  220. package/dist/state/database.test.d.ts.map +0 -1
  221. package/dist/state/database.test.js +0 -34
  222. package/dist/state/database.test.js.map +0 -1
  223. package/dist/transcription/whisperService.d.ts +0 -27
  224. package/dist/transcription/whisperService.d.ts.map +0 -1
  225. package/dist/transcription/whisperService.js +0 -102
  226. package/dist/transcription/whisperService.js.map +0 -1
  227. package/eslint.config.js +0 -55
  228. package/src/__fixtures__/highlevel-post-response.json +0 -68
  229. package/src/__fixtures__/hls-master-playlist.m3u8 +0 -24
  230. package/src/cli/commands/__snapshots__/syncHighLevel.test.ts.snap +0 -38
  231. package/src/cli/commands/config.ts +0 -74
  232. package/src/cli/commands/inspect.ts +0 -441
  233. package/src/cli/commands/login.ts +0 -68
  234. package/src/cli/commands/status.ts +0 -147
  235. package/src/cli/commands/sync.ts +0 -1235
  236. package/src/cli/commands/syncHighLevel.test.ts +0 -144
  237. package/src/cli/commands/syncHighLevel.ts +0 -639
  238. package/src/cli/index.ts +0 -121
  239. package/src/config/configManager.ts +0 -75
  240. package/src/config/paths.test.ts +0 -83
  241. package/src/config/paths.ts +0 -36
  242. package/src/config/schema.test.ts +0 -173
  243. package/src/config/schema.ts +0 -65
  244. package/src/downloader/hlsDownloader.test.ts +0 -148
  245. package/src/downloader/hlsDownloader.ts +0 -327
  246. package/src/downloader/hlsValidator.ts +0 -196
  247. package/src/downloader/index.ts +0 -122
  248. package/src/downloader/loomDownloader.test.ts +0 -43
  249. package/src/downloader/loomDownloader.ts +0 -742
  250. package/src/downloader/queue.test.ts +0 -199
  251. package/src/downloader/queue.ts +0 -118
  252. package/src/downloader/vimeoDownloader.test.ts +0 -62
  253. package/src/downloader/vimeoDownloader.ts +0 -722
  254. package/src/scraper/extractor.test.ts +0 -124
  255. package/src/scraper/extractor.ts +0 -757
  256. package/src/scraper/highlevel/__snapshots__/extractor.test.ts.snap +0 -41
  257. package/src/scraper/highlevel/extractor.test.ts +0 -134
  258. package/src/scraper/highlevel/extractor.ts +0 -537
  259. package/src/scraper/highlevel/index.ts +0 -2
  260. package/src/scraper/highlevel/navigator.test.ts +0 -110
  261. package/src/scraper/highlevel/navigator.ts +0 -668
  262. package/src/scraper/highlevel/schemas.ts +0 -183
  263. package/src/scraper/navigator.test.ts +0 -122
  264. package/src/scraper/navigator.ts +0 -355
  265. package/src/scraper/schemas.ts +0 -177
  266. package/src/scraper/videoInterceptor.ts +0 -435
  267. package/src/shared/auth.test.ts +0 -58
  268. package/src/shared/auth.ts +0 -251
  269. package/src/shared/firebase.ts +0 -151
  270. package/src/shared/fs.ts +0 -80
  271. package/src/shared/http.ts +0 -34
  272. package/src/shared/index.ts +0 -6
  273. package/src/shared/url.test.ts +0 -122
  274. package/src/state/database.test.ts +0 -49
  275. package/src/state/database.ts +0 -919
  276. package/src/state/index.ts +0 -14
  277. package/src/storage/fileSystem.test.ts +0 -64
  278. package/src/storage/fileSystem.ts +0 -175
  279. package/tsconfig.json +0 -28
  280. package/vitest.config.ts +0 -29
@@ -1,14 +0,0 @@
1
- export {
2
- CourseDatabase,
3
- LessonStatus,
4
- VideoType,
5
- extractCommunitySlug,
6
- getDbDir,
7
- getDbPath,
8
- type CourseMetadata,
9
- type LessonRecord,
10
- type LessonStatusType,
11
- type LessonWithModule,
12
- type ModuleRecord,
13
- type VideoTypeValue,
14
- } from "./database.js";
@@ -1,64 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- getLessonBasename,
4
- getVideoPath,
5
- getMarkdownPath,
6
- getDownloadFilePath,
7
- } from "./fileSystem.js";
8
-
9
- describe("fileSystem", () => {
10
- describe("getLessonBasename", () => {
11
- it("creates numbered filename from lesson name", () => {
12
- expect(getLessonBasename(0, "Introduction")).toBe("01-introduction");
13
- expect(getLessonBasename(9, "Final Lesson")).toBe("10-final-lesson");
14
- expect(getLessonBasename(99, "Bonus")).toBe("100-bonus");
15
- });
16
-
17
- it("handles special characters", () => {
18
- expect(getLessonBasename(0, "What's Next?")).toBe("01-whats-next");
19
- expect(getLessonBasename(0, "Module 1: Basics")).toBe("01-module-1-basics");
20
- });
21
- });
22
-
23
- describe("getVideoPath", () => {
24
- it("creates video path with .mp4 extension", () => {
25
- const path = getVideoPath("/courses/my-course/01-intro", 0, "Welcome");
26
- expect(path).toBe("/courses/my-course/01-intro/01-welcome.mp4");
27
- });
28
-
29
- it("handles nested paths", () => {
30
- const path = getVideoPath("/home/user/Downloads/courses/test", 5, "Lesson Six");
31
- expect(path).toBe("/home/user/Downloads/courses/test/06-lesson-six.mp4");
32
- });
33
- });
34
-
35
- describe("getMarkdownPath", () => {
36
- it("creates markdown path with .md extension", () => {
37
- const path = getMarkdownPath("/courses/my-course/01-intro", 0, "Welcome");
38
- expect(path).toBe("/courses/my-course/01-intro/01-welcome.md");
39
- });
40
- });
41
-
42
- describe("getDownloadFilePath", () => {
43
- it("prefixes filename with lesson basename", () => {
44
- const path = getDownloadFilePath("/module", 2, "Resources", "workbook.pdf");
45
- expect(path).toBe("/module/03-resources-workbook.pdf");
46
- });
47
-
48
- it("sanitizes dangerous filename characters", () => {
49
- // Characters that are invalid in filenames on various OS
50
- const path = getDownloadFilePath("/module", 0, "Intro", 'file<>:"/\\|?*.pdf');
51
- expect(path).toBe("/module/01-intro-file_________.pdf");
52
- });
53
-
54
- it("preserves safe special characters", () => {
55
- const path = getDownloadFilePath("/module", 0, "Intro", "my-file_v2 (1).pdf");
56
- expect(path).toBe("/module/01-intro-my-file_v2 (1).pdf");
57
- });
58
-
59
- it("handles filenames with multiple extensions", () => {
60
- const path = getDownloadFilePath("/module", 0, "Intro", "archive.tar.gz");
61
- expect(path).toBe("/module/01-intro-archive.tar.gz");
62
- });
63
- });
64
- });
@@ -1,175 +0,0 @@
1
- import { createWriteStream } from "node:fs";
2
- import { join } from "node:path";
3
- import { Readable } from "node:stream";
4
- import { pipeline } from "node:stream/promises";
5
- import { expandPath, getSyncStatePath } from "../config/paths.js";
6
- import type { CourseSyncState } from "../config/schema.js";
7
- import { courseSyncStateSchema } from "../config/schema.js";
8
- import { createFolderName } from "../scraper/navigator.js";
9
- import { ensureDir, outputFile, pathExists, readJson, outputJson } from "../shared/fs.js";
10
- import { http } from "../shared/http.js";
11
-
12
- // ============================================
13
- // Pure functions - testable without mocking
14
- // ============================================
15
-
16
- /**
17
- * Gets the base filename for a lesson (without extension).
18
- * Format: "01-lesson-name"
19
- */
20
- export function getLessonBasename(lessonIndex: number, lessonName: string): string {
21
- return createFolderName(lessonIndex, lessonName);
22
- }
23
-
24
- /**
25
- * Gets the video file path for a lesson.
26
- * Videos are stored directly in the module directory with lesson name.
27
- */
28
- export function getVideoPath(moduleDir: string, lessonIndex: number, lessonName: string): string {
29
- return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.mp4`);
30
- }
31
-
32
- /**
33
- * Gets the markdown file path for a lesson.
34
- * Markdown files are stored directly in the module directory with lesson name.
35
- */
36
- export function getMarkdownPath(
37
- moduleDir: string,
38
- lessonIndex: number,
39
- lessonName: string
40
- ): string {
41
- return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.md`);
42
- }
43
-
44
- /**
45
- * Gets the path for a downloadable file.
46
- * Files are stored in the module directory with lesson prefix.
47
- */
48
- export function getDownloadFilePath(
49
- moduleDir: string,
50
- lessonIndex: number,
51
- lessonName: string,
52
- filename: string
53
- ): string {
54
- const lessonPrefix = getLessonBasename(lessonIndex, lessonName);
55
- // Sanitize filename
56
- const safeFilename = filename.replace(/[<>:"/\\|?*]/g, "_");
57
- return join(moduleDir, `${lessonPrefix}-${safeFilename}`);
58
- }
59
-
60
- // ============================================
61
- // I/O functions - require filesystem access
62
- // ============================================
63
- /* v8 ignore start */
64
-
65
- /**
66
- * Creates the output directory structure for a course.
67
- */
68
- export async function createCourseDirectory(
69
- outputBase: string,
70
- courseName: string
71
- ): Promise<string> {
72
- const expanded = expandPath(outputBase);
73
- const courseDir = join(expanded, createFolderName(0, courseName).replace(/^\d+-/, ""));
74
- await ensureDir(courseDir);
75
- return courseDir;
76
- }
77
-
78
- /**
79
- * Creates a module directory within a course.
80
- */
81
- export async function createModuleDirectory(
82
- courseDir: string,
83
- moduleIndex: number,
84
- moduleName: string
85
- ): Promise<string> {
86
- const moduleDir = join(courseDir, createFolderName(moduleIndex, moduleName));
87
- await ensureDir(moduleDir);
88
- return moduleDir;
89
- }
90
-
91
- /**
92
- * Saves markdown content to a file.
93
- */
94
- export async function saveMarkdown(
95
- directory: string,
96
- filename: string,
97
- content: string
98
- ): Promise<string> {
99
- const filePath = join(directory, filename);
100
- await outputFile(filePath, content);
101
- return filePath;
102
- }
103
-
104
- /**
105
- * Loads the sync state for a course.
106
- */
107
- export async function loadSyncState(courseSlug: string): Promise<CourseSyncState | null> {
108
- const statePath = getSyncStatePath(courseSlug);
109
- const data = await readJson(statePath);
110
-
111
- if (!data) {
112
- return null;
113
- }
114
-
115
- try {
116
- return courseSyncStateSchema.parse(data);
117
- } catch {
118
- return null;
119
- }
120
- }
121
-
122
- /**
123
- * Saves the sync state for a course.
124
- */
125
- export async function saveSyncState(courseSlug: string, state: CourseSyncState): Promise<void> {
126
- const statePath = getSyncStatePath(courseSlug);
127
- await outputJson(statePath, state);
128
- }
129
-
130
- /**
131
- * Checks if a lesson has been fully synced.
132
- */
133
- export async function isLessonSynced(
134
- moduleDir: string,
135
- lessonIndex: number,
136
- lessonName: string
137
- ): Promise<{ video: boolean; content: boolean }> {
138
- const [video, content] = await Promise.all([
139
- pathExists(getVideoPath(moduleDir, lessonIndex, lessonName)),
140
- pathExists(getMarkdownPath(moduleDir, lessonIndex, lessonName)),
141
- ]);
142
- return { video, content };
143
- }
144
-
145
- /**
146
- * Downloads a file from a URL to the specified path.
147
- */
148
- export async function downloadFile(
149
- url: string,
150
- outputPath: string
151
- ): Promise<{ success: boolean; error?: string }> {
152
- if (await pathExists(outputPath)) {
153
- return { success: true }; // Already downloaded
154
- }
155
-
156
- await ensureDir(join(outputPath, ".."));
157
-
158
- try {
159
- const response = await http.get(url);
160
- const body = response.body;
161
-
162
- if (!body) {
163
- return { success: false, error: "No response body" };
164
- }
165
-
166
- const fileStream = createWriteStream(outputPath);
167
- await pipeline(Readable.fromWeb(body as import("stream/web").ReadableStream), fileStream);
168
-
169
- return { success: true };
170
- } catch (error) {
171
- return { success: false, error: String(error) };
172
- }
173
- }
174
-
175
- /* v8 ignore stop */
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2024",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "lib": ["ES2024", "DOM"],
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true,
16
- "resolveJsonModule": true,
17
- "noUncheckedIndexedAccess": true,
18
- "noImplicitReturns": true,
19
- "noFallthroughCasesInSwitch": true,
20
- "noUnusedLocals": true,
21
- "noUnusedParameters": true,
22
- "exactOptionalPropertyTypes": true,
23
- "isolatedModules": true
24
- },
25
- "include": ["src/**/*"],
26
- "exclude": ["node_modules", "dist"]
27
- }
28
-
package/vitest.config.ts DELETED
@@ -1,29 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: "node",
7
- include: ["src/**/*.test.ts"],
8
- coverage: {
9
- provider: "v8",
10
- reporter: ["text", "html", "lcov"],
11
- include: ["src/**/*.ts"],
12
- exclude: [
13
- // Test files
14
- "src/**/*.test.ts",
15
- // CLI commands (interactive, hard to test)
16
- "src/cli/**",
17
- // Re-export index files
18
- "src/**/index.ts",
19
- // Pure I/O wrappers (testing would just test Node.js/packages)
20
- "src/shared/fs.ts",
21
- "src/config/configManager.ts",
22
- // Browser automation (requires Playwright, not unit testable)
23
- "src/scraper/videoInterceptor.ts",
24
- // Network validation (requires live connections)
25
- "src/downloader/hlsValidator.ts",
26
- ],
27
- },
28
- },
29
- });