offcourse 1.0.0 → 1.0.1

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 (258) hide show
  1. package/dist/cli/commands/config.js.map +1 -1
  2. package/dist/cli/commands/inspect.js +1 -1
  3. package/dist/cli/commands/inspect.js.map +1 -1
  4. package/dist/cli/commands/sync.d.ts +1 -2
  5. package/dist/cli/commands/sync.d.ts.map +1 -1
  6. package/dist/cli/commands/sync.js +13 -14
  7. package/dist/cli/commands/sync.js.map +1 -1
  8. package/dist/cli/commands/syncHighLevel.d.ts +1 -2
  9. package/dist/cli/commands/syncHighLevel.d.ts.map +1 -1
  10. package/dist/cli/commands/syncHighLevel.js +4 -8
  11. package/dist/cli/commands/syncHighLevel.js.map +1 -1
  12. package/dist/cli/index.js +1 -1
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/config/configManager.d.ts.map +1 -1
  15. package/dist/config/configManager.js +4 -0
  16. package/dist/config/configManager.js.map +1 -1
  17. package/dist/downloader/hlsDownloader.d.ts.map +1 -1
  18. package/dist/downloader/hlsDownloader.js +23 -14
  19. package/dist/downloader/hlsDownloader.js.map +1 -1
  20. package/dist/downloader/hlsValidator.d.ts.map +1 -1
  21. package/dist/downloader/hlsValidator.js +6 -2
  22. package/dist/downloader/hlsValidator.js.map +1 -1
  23. package/dist/downloader/index.d.ts +3 -0
  24. package/dist/downloader/index.d.ts.map +1 -1
  25. package/dist/downloader/index.js +3 -0
  26. package/dist/downloader/index.js.map +1 -1
  27. package/dist/downloader/loomDownloader.d.ts.map +1 -1
  28. package/dist/downloader/loomDownloader.js +23 -20
  29. package/dist/downloader/loomDownloader.js.map +1 -1
  30. package/dist/downloader/queue.d.ts +4 -4
  31. package/dist/downloader/queue.d.ts.map +1 -1
  32. package/dist/downloader/queue.js.map +1 -1
  33. package/dist/downloader/vimeoDownloader.d.ts.map +1 -1
  34. package/dist/downloader/vimeoDownloader.js +7 -3
  35. package/dist/downloader/vimeoDownloader.js.map +1 -1
  36. package/dist/scraper/extractor.d.ts +4 -0
  37. package/dist/scraper/extractor.d.ts.map +1 -1
  38. package/dist/scraper/extractor.js +79 -79
  39. package/dist/scraper/extractor.js.map +1 -1
  40. package/dist/scraper/highlevel/extractor.d.ts +11 -19
  41. package/dist/scraper/highlevel/extractor.d.ts.map +1 -1
  42. package/dist/scraper/highlevel/extractor.js +72 -85
  43. package/dist/scraper/highlevel/extractor.js.map +1 -1
  44. package/dist/scraper/highlevel/navigator.d.ts +3 -10
  45. package/dist/scraper/highlevel/navigator.d.ts.map +1 -1
  46. package/dist/scraper/highlevel/navigator.js +140 -127
  47. package/dist/scraper/highlevel/navigator.js.map +1 -1
  48. package/dist/scraper/highlevel/schemas.d.ts +188 -0
  49. package/dist/scraper/highlevel/schemas.d.ts.map +1 -0
  50. package/dist/scraper/highlevel/schemas.js +139 -0
  51. package/dist/scraper/highlevel/schemas.js.map +1 -0
  52. package/dist/scraper/navigator.d.ts +14 -11
  53. package/dist/scraper/navigator.d.ts.map +1 -1
  54. package/dist/scraper/navigator.js +61 -104
  55. package/dist/scraper/navigator.js.map +1 -1
  56. package/dist/scraper/schemas.d.ts +57 -0
  57. package/dist/scraper/schemas.d.ts.map +1 -0
  58. package/dist/scraper/schemas.js +135 -0
  59. package/dist/scraper/schemas.js.map +1 -0
  60. package/dist/scraper/videoInterceptor.d.ts +4 -0
  61. package/dist/scraper/videoInterceptor.d.ts.map +1 -1
  62. package/dist/scraper/videoInterceptor.js +66 -51
  63. package/dist/scraper/videoInterceptor.js.map +1 -1
  64. package/dist/shared/auth.d.ts +9 -9
  65. package/dist/shared/auth.d.ts.map +1 -1
  66. package/dist/shared/auth.js +24 -38
  67. package/dist/shared/auth.js.map +1 -1
  68. package/dist/shared/firebase.d.ts +60 -0
  69. package/dist/shared/firebase.d.ts.map +1 -0
  70. package/dist/shared/firebase.js +102 -0
  71. package/dist/shared/firebase.js.map +1 -0
  72. package/dist/shared/fs.d.ts.map +1 -1
  73. package/dist/shared/fs.js +4 -0
  74. package/dist/shared/fs.js.map +1 -1
  75. package/dist/shared/index.d.ts +3 -0
  76. package/dist/shared/index.d.ts.map +1 -1
  77. package/dist/shared/index.js +3 -0
  78. package/dist/shared/index.js.map +1 -1
  79. package/dist/shared/slug.d.ts +11 -0
  80. package/dist/shared/slug.d.ts.map +1 -0
  81. package/{src/shared/slug.ts → dist/shared/slug.js} +10 -11
  82. package/dist/shared/slug.js.map +1 -0
  83. package/dist/shared/url.d.ts +43 -0
  84. package/dist/shared/url.d.ts.map +1 -0
  85. package/{src/shared/url.ts → dist/shared/url.js} +12 -15
  86. package/dist/shared/url.js.map +1 -0
  87. package/dist/state/database.d.ts +1 -0
  88. package/dist/state/database.d.ts.map +1 -1
  89. package/dist/state/database.js +3 -0
  90. package/dist/state/database.js.map +1 -1
  91. package/dist/storage/fileSystem.d.ts +17 -17
  92. package/dist/storage/fileSystem.d.ts.map +1 -1
  93. package/dist/storage/fileSystem.js +39 -31
  94. package/dist/storage/fileSystem.js.map +1 -1
  95. package/package.json +5 -2
  96. package/.github/workflows/ci.yml +0 -50
  97. package/.husky/commit-msg +0 -2
  98. package/.husky/pre-commit +0 -1
  99. package/.husky/pre-push +0 -3
  100. package/.prettierrc +0 -8
  101. package/.release-it.json +0 -23
  102. package/ARCHITECTURE.md +0 -233
  103. package/CHANGELOG.md +0 -78
  104. package/commitlint.config.js +0 -4
  105. package/dist/ai/openRouter.d.ts +0 -47
  106. package/dist/ai/openRouter.d.ts.map +0 -1
  107. package/dist/ai/openRouter.js +0 -116
  108. package/dist/ai/openRouter.js.map +0 -1
  109. package/dist/ai/transcriptPolisher.d.ts +0 -24
  110. package/dist/ai/transcriptPolisher.d.ts.map +0 -1
  111. package/dist/ai/transcriptPolisher.js +0 -89
  112. package/dist/ai/transcriptPolisher.js.map +0 -1
  113. package/dist/cli/commands/enrich.d.ts +0 -14
  114. package/dist/cli/commands/enrich.d.ts.map +0 -1
  115. package/dist/cli/commands/enrich.js +0 -271
  116. package/dist/cli/commands/enrich.js.map +0 -1
  117. package/dist/cli/commands/syncGhl.d.ts +0 -20
  118. package/dist/cli/commands/syncGhl.d.ts.map +0 -1
  119. package/dist/cli/commands/syncGhl.js +0 -483
  120. package/dist/cli/commands/syncGhl.js.map +0 -1
  121. package/dist/cli/commands/syncHighLevel.test.d.ts +0 -2
  122. package/dist/cli/commands/syncHighLevel.test.d.ts.map +0 -1
  123. package/dist/cli/commands/syncHighLevel.test.js +0 -102
  124. package/dist/cli/commands/syncHighLevel.test.js.map +0 -1
  125. package/dist/config/paths.test.d.ts +0 -2
  126. package/dist/config/paths.test.d.ts.map +0 -1
  127. package/dist/config/paths.test.js +0 -70
  128. package/dist/config/paths.test.js.map +0 -1
  129. package/dist/config/schema.test.d.ts +0 -2
  130. package/dist/config/schema.test.d.ts.map +0 -1
  131. package/dist/config/schema.test.js +0 -151
  132. package/dist/config/schema.test.js.map +0 -1
  133. package/dist/downloader/hlsDownloader.test.d.ts +0 -2
  134. package/dist/downloader/hlsDownloader.test.d.ts.map +0 -1
  135. package/dist/downloader/hlsDownloader.test.js +0 -116
  136. package/dist/downloader/hlsDownloader.test.js.map +0 -1
  137. package/dist/downloader/loomDownloader.test.d.ts +0 -2
  138. package/dist/downloader/loomDownloader.test.d.ts.map +0 -1
  139. package/dist/downloader/loomDownloader.test.js +0 -36
  140. package/dist/downloader/loomDownloader.test.js.map +0 -1
  141. package/dist/downloader/queue.test.d.ts +0 -2
  142. package/dist/downloader/queue.test.d.ts.map +0 -1
  143. package/dist/downloader/queue.test.js +0 -158
  144. package/dist/downloader/queue.test.js.map +0 -1
  145. package/dist/downloader/videoDownloader.d.ts +0 -32
  146. package/dist/downloader/videoDownloader.d.ts.map +0 -1
  147. package/dist/downloader/videoDownloader.js +0 -173
  148. package/dist/downloader/videoDownloader.js.map +0 -1
  149. package/dist/downloader/vimeoDownloader.test.d.ts +0 -2
  150. package/dist/downloader/vimeoDownloader.test.d.ts.map +0 -1
  151. package/dist/downloader/vimeoDownloader.test.js +0 -51
  152. package/dist/downloader/vimeoDownloader.test.js.map +0 -1
  153. package/dist/scraper/auth.d.ts +0 -29
  154. package/dist/scraper/auth.d.ts.map +0 -1
  155. package/dist/scraper/auth.js +0 -115
  156. package/dist/scraper/auth.js.map +0 -1
  157. package/dist/scraper/extractor.test.d.ts +0 -2
  158. package/dist/scraper/extractor.test.d.ts.map +0 -1
  159. package/dist/scraper/extractor.test.js +0 -65
  160. package/dist/scraper/extractor.test.js.map +0 -1
  161. package/dist/scraper/ghl/auth.d.ts +0 -25
  162. package/dist/scraper/ghl/auth.d.ts.map +0 -1
  163. package/dist/scraper/ghl/auth.js +0 -187
  164. package/dist/scraper/ghl/auth.js.map +0 -1
  165. package/dist/scraper/ghl/extractor.d.ts +0 -96
  166. package/dist/scraper/ghl/extractor.d.ts.map +0 -1
  167. package/dist/scraper/ghl/extractor.js +0 -345
  168. package/dist/scraper/ghl/extractor.js.map +0 -1
  169. package/dist/scraper/ghl/index.d.ts +0 -4
  170. package/dist/scraper/ghl/index.d.ts.map +0 -1
  171. package/dist/scraper/ghl/index.js +0 -4
  172. package/dist/scraper/ghl/index.js.map +0 -1
  173. package/dist/scraper/ghl/navigator.d.ts +0 -93
  174. package/dist/scraper/ghl/navigator.d.ts.map +0 -1
  175. package/dist/scraper/ghl/navigator.js +0 -447
  176. package/dist/scraper/ghl/navigator.js.map +0 -1
  177. package/dist/scraper/highlevel/auth.d.ts +0 -25
  178. package/dist/scraper/highlevel/auth.d.ts.map +0 -1
  179. package/dist/scraper/highlevel/auth.js +0 -189
  180. package/dist/scraper/highlevel/auth.js.map +0 -1
  181. package/dist/scraper/highlevel/extractor.test.d.ts +0 -2
  182. package/dist/scraper/highlevel/extractor.test.d.ts.map +0 -1
  183. package/dist/scraper/highlevel/extractor.test.js +0 -101
  184. package/dist/scraper/highlevel/extractor.test.js.map +0 -1
  185. package/dist/scraper/highlevel/navigator.test.d.ts +0 -2
  186. package/dist/scraper/highlevel/navigator.test.d.ts.map +0 -1
  187. package/dist/scraper/highlevel/navigator.test.js +0 -78
  188. package/dist/scraper/highlevel/navigator.test.js.map +0 -1
  189. package/dist/scraper/navigator.test.d.ts +0 -2
  190. package/dist/scraper/navigator.test.d.ts.map +0 -1
  191. package/dist/scraper/navigator.test.js +0 -63
  192. package/dist/scraper/navigator.test.js.map +0 -1
  193. package/dist/scraper/skoolApi.d.ts +0 -17
  194. package/dist/scraper/skoolApi.d.ts.map +0 -1
  195. package/dist/scraper/skoolApi.js +0 -72
  196. package/dist/scraper/skoolApi.js.map +0 -1
  197. package/dist/state/database.test.d.ts +0 -2
  198. package/dist/state/database.test.d.ts.map +0 -1
  199. package/dist/state/database.test.js +0 -34
  200. package/dist/state/database.test.js.map +0 -1
  201. package/dist/transcription/whisperService.d.ts +0 -27
  202. package/dist/transcription/whisperService.d.ts.map +0 -1
  203. package/dist/transcription/whisperService.js +0 -102
  204. package/dist/transcription/whisperService.js.map +0 -1
  205. package/eslint.config.js +0 -55
  206. package/src/__fixtures__/highlevel-post-response.json +0 -68
  207. package/src/__fixtures__/hls-master-playlist.m3u8 +0 -24
  208. package/src/cli/commands/__snapshots__/syncHighLevel.test.ts.snap +0 -38
  209. package/src/cli/commands/config.ts +0 -74
  210. package/src/cli/commands/inspect.ts +0 -441
  211. package/src/cli/commands/login.ts +0 -68
  212. package/src/cli/commands/status.ts +0 -147
  213. package/src/cli/commands/sync.ts +0 -1235
  214. package/src/cli/commands/syncHighLevel.test.ts +0 -144
  215. package/src/cli/commands/syncHighLevel.ts +0 -639
  216. package/src/cli/index.ts +0 -121
  217. package/src/config/configManager.ts +0 -75
  218. package/src/config/paths.test.ts +0 -83
  219. package/src/config/paths.ts +0 -36
  220. package/src/config/schema.test.ts +0 -173
  221. package/src/config/schema.ts +0 -65
  222. package/src/downloader/hlsDownloader.test.ts +0 -148
  223. package/src/downloader/hlsDownloader.ts +0 -327
  224. package/src/downloader/hlsValidator.ts +0 -196
  225. package/src/downloader/index.ts +0 -122
  226. package/src/downloader/loomDownloader.test.ts +0 -43
  227. package/src/downloader/loomDownloader.ts +0 -742
  228. package/src/downloader/queue.test.ts +0 -199
  229. package/src/downloader/queue.ts +0 -118
  230. package/src/downloader/vimeoDownloader.test.ts +0 -62
  231. package/src/downloader/vimeoDownloader.ts +0 -722
  232. package/src/scraper/extractor.test.ts +0 -124
  233. package/src/scraper/extractor.ts +0 -757
  234. package/src/scraper/highlevel/__snapshots__/extractor.test.ts.snap +0 -41
  235. package/src/scraper/highlevel/extractor.test.ts +0 -134
  236. package/src/scraper/highlevel/extractor.ts +0 -537
  237. package/src/scraper/highlevel/index.ts +0 -2
  238. package/src/scraper/highlevel/navigator.test.ts +0 -110
  239. package/src/scraper/highlevel/navigator.ts +0 -668
  240. package/src/scraper/highlevel/schemas.ts +0 -183
  241. package/src/scraper/navigator.test.ts +0 -122
  242. package/src/scraper/navigator.ts +0 -355
  243. package/src/scraper/schemas.ts +0 -177
  244. package/src/scraper/videoInterceptor.ts +0 -435
  245. package/src/shared/auth.test.ts +0 -58
  246. package/src/shared/auth.ts +0 -251
  247. package/src/shared/firebase.ts +0 -151
  248. package/src/shared/fs.ts +0 -80
  249. package/src/shared/http.ts +0 -34
  250. package/src/shared/index.ts +0 -6
  251. package/src/shared/url.test.ts +0 -122
  252. package/src/state/database.test.ts +0 -49
  253. package/src/state/database.ts +0 -919
  254. package/src/state/index.ts +0 -14
  255. package/src/storage/fileSystem.test.ts +0 -64
  256. package/src/storage/fileSystem.ts +0 -175
  257. package/tsconfig.json +0 -28
  258. package/vitest.config.ts +0 -29
@@ -7,6 +7,44 @@ import { courseSyncStateSchema } from "../config/schema.js";
7
7
  import { createFolderName } from "../scraper/navigator.js";
8
8
  import { ensureDir, outputFile, pathExists, readJson, outputJson } from "../shared/fs.js";
9
9
  import { http } from "../shared/http.js";
10
+ // ============================================
11
+ // Pure functions - testable without mocking
12
+ // ============================================
13
+ /**
14
+ * Gets the base filename for a lesson (without extension).
15
+ * Format: "01-lesson-name"
16
+ */
17
+ export function getLessonBasename(lessonIndex, lessonName) {
18
+ return createFolderName(lessonIndex, lessonName);
19
+ }
20
+ /**
21
+ * Gets the video file path for a lesson.
22
+ * Videos are stored directly in the module directory with lesson name.
23
+ */
24
+ export function getVideoPath(moduleDir, lessonIndex, lessonName) {
25
+ return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.mp4`);
26
+ }
27
+ /**
28
+ * Gets the markdown file path for a lesson.
29
+ * Markdown files are stored directly in the module directory with lesson name.
30
+ */
31
+ export function getMarkdownPath(moduleDir, lessonIndex, lessonName) {
32
+ return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.md`);
33
+ }
34
+ /**
35
+ * Gets the path for a downloadable file.
36
+ * Files are stored in the module directory with lesson prefix.
37
+ */
38
+ export function getDownloadFilePath(moduleDir, lessonIndex, lessonName, filename) {
39
+ const lessonPrefix = getLessonBasename(lessonIndex, lessonName);
40
+ // Sanitize filename
41
+ const safeFilename = filename.replace(/[<>:"/\\|?*]/g, "_");
42
+ return join(moduleDir, `${lessonPrefix}-${safeFilename}`);
43
+ }
44
+ // ============================================
45
+ // I/O functions - require filesystem access
46
+ // ============================================
47
+ /* v8 ignore start */
10
48
  /**
11
49
  * Creates the output directory structure for a course.
12
50
  */
@@ -24,13 +62,6 @@ export async function createModuleDirectory(courseDir, moduleIndex, moduleName)
24
62
  await ensureDir(moduleDir);
25
63
  return moduleDir;
26
64
  }
27
- /**
28
- * Gets the base filename for a lesson (without extension).
29
- * Format: "01-lesson-name"
30
- */
31
- export function getLessonBasename(lessonIndex, lessonName) {
32
- return createFolderName(lessonIndex, lessonName);
33
- }
34
65
  /**
35
66
  * Saves markdown content to a file.
36
67
  */
@@ -39,20 +70,6 @@ export async function saveMarkdown(directory, filename, content) {
39
70
  await outputFile(filePath, content);
40
71
  return filePath;
41
72
  }
42
- /**
43
- * Gets the video file path for a lesson.
44
- * Videos are stored directly in the module directory with lesson name.
45
- */
46
- export function getVideoPath(moduleDir, lessonIndex, lessonName) {
47
- return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.mp4`);
48
- }
49
- /**
50
- * Gets the markdown file path for a lesson.
51
- * Markdown files are stored directly in the module directory with lesson name.
52
- */
53
- export function getMarkdownPath(moduleDir, lessonIndex, lessonName) {
54
- return join(moduleDir, `${getLessonBasename(lessonIndex, lessonName)}.md`);
55
- }
56
73
  /**
57
74
  * Loads the sync state for a course.
58
75
  */
@@ -86,16 +103,6 @@ export async function isLessonSynced(moduleDir, lessonIndex, lessonName) {
86
103
  ]);
87
104
  return { video, content };
88
105
  }
89
- /**
90
- * Gets the path for a downloadable file.
91
- * Files are stored in the module directory with lesson prefix.
92
- */
93
- export function getDownloadFilePath(moduleDir, lessonIndex, lessonName, filename) {
94
- const lessonPrefix = getLessonBasename(lessonIndex, lessonName);
95
- // Sanitize filename
96
- const safeFilename = filename.replace(/[<>:"/\\|?*]/g, "_");
97
- return join(moduleDir, `${lessonPrefix}-${safeFilename}`);
98
- }
99
106
  /**
100
107
  * Downloads a file from a URL to the specified path.
101
108
  */
@@ -118,4 +125,5 @@ export async function downloadFile(url, outputPath) {
118
125
  return { success: false, error: String(error) };
119
126
  }
120
127
  }
128
+ /* v8 ignore stop */
121
129
  //# sourceMappingURL=fileSystem.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fileSystem.js","sourceRoot":"","sources":["../../src/storage/fileSystem.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fileSystem.js","sourceRoot":"","sources":["../../src/storage/fileSystem.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "offcourse",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Download online courses for offline access – of course!",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "offcourse": "./dist/cli/index.js"
8
8
  },
9
+ "files": [
10
+ "dist"
11
+ ],
9
12
  "repository": {
10
13
  "type": "git",
11
14
  "url": "git+https://github.com/sebastian-software/offcourse.git"
@@ -15,7 +18,7 @@
15
18
  },
16
19
  "homepage": "https://offcourse.app",
17
20
  "scripts": {
18
- "build": "tsc",
21
+ "build": "tsc -p tsconfig.build.json",
19
22
  "dev": "tsc --watch",
20
23
  "start": "node dist/cli/index.js",
21
24
  "lint": "eslint src",
@@ -1,50 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
-
9
- jobs:
10
- test:
11
- runs-on: ${{ matrix.os }}
12
-
13
- strategy:
14
- matrix:
15
- os: [ubuntu-latest, macos-latest, windows-latest]
16
- node-version: [22, 24]
17
-
18
- steps:
19
- - name: Checkout repository
20
- uses: actions/checkout@v4
21
-
22
- - name: Setup Node.js ${{ matrix.node-version }}
23
- uses: actions/setup-node@v4
24
- with:
25
- node-version: ${{ matrix.node-version }}
26
- cache: "npm"
27
-
28
- - name: Install dependencies
29
- run: npm ci
30
-
31
- - name: Type check
32
- run: npm run typecheck
33
-
34
- - name: Lint
35
- run: npm run lint
36
-
37
- - name: Run tests with coverage
38
- run: npm run test:coverage
39
-
40
- - name: Upload coverage to Codecov
41
- if: matrix.os == 'ubuntu-latest' && matrix.node-version == 22
42
- uses: codecov/codecov-action@v5
43
- with:
44
- token: ${{ secrets.CODECOV_TOKEN }}
45
- files: ./coverage/lcov.info
46
- fail_ci_if_error: false
47
- verbose: true
48
-
49
-
50
-
package/.husky/commit-msg DELETED
@@ -1,2 +0,0 @@
1
- npx --no -- commitlint --edit $1
2
-
package/.husky/pre-commit DELETED
@@ -1 +0,0 @@
1
- npx lint-staged
package/.husky/pre-push DELETED
@@ -1,3 +0,0 @@
1
- npm run lint
2
- npm run typecheck
3
-
package/.prettierrc DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "semi": true,
3
- "singleQuote": false,
4
- "tabWidth": 2,
5
- "trailingComma": "es5",
6
- "printWidth": 100
7
- }
8
-
package/.release-it.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "git": {
3
- "commitMessage": "chore: release v${version}",
4
- "tagName": "v${version}",
5
- "requireBranch": "main"
6
- },
7
- "github": {
8
- "release": true,
9
- "releaseName": "v${version}"
10
- },
11
- "npm": {
12
- "publish": true
13
- },
14
- "plugins": {
15
- "@release-it/conventional-changelog": {
16
- "preset": "conventionalcommits",
17
- "infile": "CHANGELOG.md"
18
- }
19
- },
20
- "hooks": {
21
- "before:init": ["npm run lint", "npm run typecheck", "npm test run"]
22
- }
23
- }
package/ARCHITECTURE.md DELETED
@@ -1,233 +0,0 @@
1
- # Architecture
2
-
3
- ## Overview
4
-
5
- Offcourse is a modular CLI tool for downloading online courses. The architecture is designed to support multiple learning platforms through a plugin-like pattern.
6
-
7
- ```
8
- ┌─────────────────────────────────────────────────────────────┐
9
- │ CLI Layer │
10
- │ (commands: login, sync, sync-skool, sync-highlevel, etc.) │
11
- └─────────────────────────────────────────────────────────────┘
12
-
13
- ┌───────────────────┼───────────────────┐
14
- ▼ ▼ ▼
15
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
16
- │ Scraper │ │ Downloader │ │ Storage │
17
- │ (per platform) │ │ (per video host)│ │ (file system) │
18
- └─────────────────┘ └─────────────────┘ └─────────────────┘
19
- │ │ │
20
- └───────────────────┼───────────────────┘
21
-
22
- ┌─────────────────┐
23
- │ Config │
24
- │ (Zod schemas) │
25
- └─────────────────┘
26
- ```
27
-
28
- ## Directory Structure
29
-
30
- ```
31
- src/
32
- ├── cli/ # Command-line interface
33
- │ ├── index.ts # Entry point, command registration
34
- │ └── commands/
35
- │ ├── config.ts # Configuration management
36
- │ ├── inspect.ts # Page analysis for debugging
37
- │ ├── login.ts # Authentication flow
38
- │ ├── sync.ts # Skool download orchestration
39
- │ └── syncHighLevel.ts # HighLevel download orchestration
40
-
41
- ├── config/ # Configuration management
42
- │ ├── schema.ts # Zod schemas for all config types
43
- │ ├── configManager.ts # Load/save configuration
44
- │ └── paths.ts # Path resolution utilities
45
-
46
- ├── scraper/ # Platform-specific extraction
47
- │ ├── auth.ts # Session management (Playwright) - Skool
48
- │ ├── navigator.ts # Course structure discovery - Skool
49
- │ ├── extractor.ts # Content extraction - Skool
50
- │ ├── videoInterceptor.ts # Network interception for video URLs
51
- │ └── highlevel/ # HighLevel (GoHighLevel) scraper
52
- │ ├── auth.ts # Firebase auth, session management
53
- │ ├── navigator.ts # Course structure via API
54
- │ ├── extractor.ts # Video/content extraction
55
- │ └── index.ts # Exports
56
-
57
- ├── downloader/ # Video download handlers
58
- │ ├── index.ts # Download dispatcher by video type
59
- │ ├── queue.ts # Async queue with concurrency control
60
- │ ├── loomDownloader.ts # Loom-specific HLS download
61
- │ ├── vimeoDownloader.ts # Vimeo-specific download
62
- │ └── hlsDownloader.ts # Generic HLS download (ffmpeg-based)
63
-
64
- ├── state/ # State management
65
- │ ├── index.ts # State exports
66
- │ └── database.ts # SQLite database for sync state
67
-
68
- └── storage/ # File system operations
69
- └── fileSystem.ts # Directory creation, file saving
70
- ```
71
-
72
- ## Key Components
73
-
74
- ### CLI Layer (`src/cli/`)
75
-
76
- Handles user interaction via Commander.js. Each command is a separate module.
77
-
78
- - **login**: Opens browser for interactive authentication, saves session
79
- - **sync**: Auto-detects platform and delegates to appropriate handler
80
- - **sync-skool**: Skool-specific sync (uses `sync.ts`)
81
- - **sync-highlevel**: HighLevel-specific sync (uses `syncHighLevel.ts`)
82
- - **inspect**: Debug tool for analyzing page structure
83
- - **config**: Read/write configuration values
84
-
85
- ### Scraper (`src/scraper/`)
86
-
87
- Platform-specific logic for extracting course content.
88
-
89
- #### Skool Scraper (root level)
90
-
91
- - **auth.ts**: Manages Playwright browser sessions, session persistence
92
- - **navigator.ts**: Discovers course structure (modules, lessons, URLs)
93
- - **extractor.ts**: Extracts video URLs and text content from lesson pages
94
- - **videoInterceptor.ts**: Intercepts network requests to capture video URLs
95
-
96
- #### HighLevel Scraper (`src/scraper/highlevel/`)
97
-
98
- - **auth.ts**: Firebase authentication, session management with token refresh
99
- - **navigator.ts**: Extracts course structure via API interception
100
- - **extractor.ts**: Extracts HLS video URLs, embedded videos (Vimeo, Loom), and content
101
-
102
- To add a new platform, create a new directory under `src/scraper/` with the same interfaces.
103
-
104
- ### Downloader (`src/downloader/`)
105
-
106
- Video download handlers. Each video host needs its own implementation.
107
-
108
- - **queue.ts**: Generic async queue with concurrency control and retry logic
109
- - **loomDownloader.ts**: Handles Loom's HLS streaming format
110
- - **vimeoDownloader.ts**: Handles Vimeo video downloads
111
- - **hlsDownloader.ts**: Generic HLS download using ffmpeg (used for HighLevel native videos)
112
- - **index.ts**: Dispatcher that routes downloads by video type
113
-
114
- ### State (`src/state/`)
115
-
116
- Persistent state management using SQLite.
117
-
118
- - **database.ts**: Manages sync state, tracks downloaded content
119
- - Enables resume functionality for interrupted syncs
120
-
121
- ### Storage (`src/storage/`)
122
-
123
- File system abstraction for saving content.
124
-
125
- - Creates directory structure mirroring course hierarchy
126
- - Saves markdown content and video files
127
- - Tracks sync state to enable resume
128
-
129
- ### Config (`src/config/`)
130
-
131
- Centralized configuration with Zod validation.
132
-
133
- - **schema.ts**: Type-safe schemas for all configuration
134
- - **configManager.ts**: Persists config to `~/.offcourse/`
135
- - **paths.ts**: Path resolution utilities
136
-
137
- ## Data Flow
138
-
139
- ```
140
- 1. User runs: offcourse sync <url>
141
-
142
- 2. Auto-detect │
143
- platform ────────────► Skool? HighLevel? Unknown?
144
-
145
- 3. Load config │
146
-
147
- 4. Authenticate ─────────► Browser session (cached or interactive)
148
-
149
- 5. Navigate ────────────► Extract course structure (modules, lessons)
150
-
151
- 6. For each lesson: │
152
- ├─► Extract ─────────► Get video URL + text content
153
- ├─► Save content ────► Write Markdown to disk
154
- └─► Queue video ─────► Add to download queue
155
-
156
- 7. Process queue ───────► Download videos with concurrency control
157
-
158
- 8. Done ────────────────► Summary output
159
- ```
160
-
161
- ## Platform-Specific Details
162
-
163
- ### Skool
164
-
165
- - **Auth**: Standard session-based, browser login
166
- - **Structure**: DOM-based extraction via Playwright selectors
167
- - **Videos**: Loom, Vimeo, native video elements
168
-
169
- ### HighLevel (GoHighLevel)
170
-
171
- - **Auth**: Firebase authentication via `sso.clientclub.net`
172
- - **Structure**: API-based extraction (`services.leadconnectorhq.com`)
173
- - **Videos**: Native HLS streams, Vimeo, Loom embeds
174
- - **Special**: Requires ffmpeg for native video downloads
175
-
176
- ## Adding a New Platform
177
-
178
- 1. **Create scraper directory**: `src/scraper/<platform>/`
179
- 2. **Implement modules**:
180
- - `auth.ts` - Authentication flow
181
- - `navigator.ts` - Course structure extraction
182
- - `extractor.ts` - Content/video extraction
183
- - `index.ts` - Exports
184
- 3. **Create CLI command**: `src/cli/commands/sync<Platform>.ts`
185
- 4. **Register in CLI**: Add to `src/cli/index.ts`
186
- 5. **Add auto-detection**: Update `sync` command's platform detection
187
-
188
- ## Adding a New Video Host
189
-
190
- 1. **Create downloader**: Implement in `src/downloader/<host>Downloader.ts`
191
- 2. **Export from index**: Add to `src/downloader/index.ts` dispatcher
192
- 3. **Update extractor**: Add detection in platform-specific `extractVideoUrl()`
193
-
194
- ## Technology Choices
195
-
196
- | Purpose | Technology | Rationale |
197
- |---------|------------|-----------|
198
- | Browser automation | Playwright | Reliable, handles SPAs, session persistence |
199
- | CLI framework | Commander.js | Standard, declarative command definition |
200
- | Validation | Zod | Runtime validation with TypeScript inference |
201
- | HTML → Markdown | Turndown | Mature, configurable |
202
- | Styling | Chalk + Ora | Clean terminal output with spinners |
203
- | Database | better-sqlite3 | Fast, embedded SQLite for state management |
204
- | HLS downloads | ffmpeg | Industry standard for HLS stream processing |
205
-
206
- ## Development Tooling
207
-
208
- | Tool | Purpose | Configuration |
209
- |------|---------|---------------|
210
- | TypeScript | Type safety | `tsconfig.json` |
211
- | ESLint | Linting | `eslint.config.js` |
212
- | Prettier | Code formatting | `.prettierrc` (defaults) |
213
- | Vitest | Testing | `vitest.config.ts` |
214
- | Husky | Git hooks | `.husky/` |
215
- | lint-staged | Pre-commit formatting | `package.json` |
216
- | commitlint | Commit message validation | `commitlint.config.js` |
217
- | release-it | Release management | `.release-it.json` |
218
-
219
- ### Git Hooks
220
-
221
- - **pre-commit**: Formats staged files with Prettier via lint-staged
222
- - **pre-push**: Runs ESLint and TypeScript type checking
223
- - **commit-msg**: Validates conventional commit format
224
-
225
- ### Release Process
226
-
227
- Releases use [release-it](https://github.com/release-it/release-it) with the conventional changelog plugin:
228
-
229
- 1. Validates code (lint, typecheck, test)
230
- 2. Determines version bump from commit history
231
- 3. Updates `CHANGELOG.md` with categorized changes
232
- 4. Creates Git tag and GitHub release
233
- 5. Publishes to npm
package/CHANGELOG.md DELETED
@@ -1,78 +0,0 @@
1
- # Changelog
2
-
3
- ## 1.0.0 (2025-12-22)
4
-
5
- ### ⚠ BREAKING CHANGES
6
-
7
- * The 'enrich' command for transcribing videos is no longer available
8
-
9
- ### Features
10
-
11
- * add --resume flag and fix HLS URL handling in downloaders ([163055b](https://github.com/sebastian-software/offcourse/commit/163055b343345aa60bf3babd1b4bb33443d61616))
12
- * add detailed error reporting and retry logic for Loom downloads ([177e92d](https://github.com/sebastian-software/offcourse/commit/177e92d5b5a30d959b6582327fb16d6337916ea1))
13
- * add fast mode to skip images, fonts, CSS during scraping ([bc7a128](https://github.com/sebastian-software/offcourse/commit/bc7a12836470b914bd1b515ab04fda5a5c6e705b))
14
- * add HighLevel (GoHighLevel) course scraper support ([04c15f8](https://github.com/sebastian-software/offcourse/commit/04c15f832381d3fbfde82e3f0847bf32f7e590c2))
15
- * add network interception fallback for video URL capture ([fc36f6d](https://github.com/sebastian-software/offcourse/commit/fc36f6d03e5d17f681114fea791454453c5e819f))
16
- * add OpenRouter integration for transcript polishing ([22487f2](https://github.com/sebastian-software/offcourse/commit/22487f2300bc1b9ac1b5be98db695094cca4bf54))
17
- * add tsx for dev, detect locked lessons ([809c9fd](https://github.com/sebastian-software/offcourse/commit/809c9fdd6ae9a06e2fa038d020bdff18f275505c))
18
- * add video transcription with Whisper ([06f9edd](https://github.com/sebastian-software/offcourse/commit/06f9eddf56c4181e4d7530c7a39ca23c25d1a0f0))
19
- * add video type prefix [LOOM], [VIMEO] etc. to output ([bba70e7](https://github.com/sebastian-software/offcourse/commit/bba70e7581c39b9fd3e9934496cd2b50106418c6))
20
- * add Vimeo video download support ([64ecde7](https://github.com/sebastian-software/offcourse/commit/64ecde76081749ff232732493ede602e7013be44))
21
- * add Zod schemas for API response validation ([1fec40f](https://github.com/sebastian-software/offcourse/commit/1fec40f49a0bd6900b46eeab5d95cbf6381e01ca))
22
- * beautiful multi-progress bars for parallel downloads ([cfe6e01](https://github.com/sebastian-software/offcourse/commit/cfe6e012eb70d283475e8963e0be08bbf24d7501))
23
- * **cli:** add Commander-based CLI with sync, login, inspect commands ([53b53e1](https://github.com/sebastian-software/offcourse/commit/53b53e10de11d3ceefabe16f17a3b1a50cec73c4))
24
- * **config:** add configuration system with Zod validation ([552e5c8](https://github.com/sebastian-software/offcourse/commit/552e5c8d3bebb60d443275b8bcf8e0bec44d38df))
25
- * detect and track locked lessons separately ([8a62c9a](https://github.com/sebastian-software/offcourse/commit/8a62c9aa860e375dee8853aeecaa244668e88fd4))
26
- * download linked PDF and Office files from lessons ([6e838be](https://github.com/sebastian-software/offcourse/commit/6e838beabc1f4e75505cffa2712f567c39be4941))
27
- * **downloader:** add HLS streaming support for Loom videos ([2dfcae1](https://github.com/sebastian-software/offcourse/commit/2dfcae1a746075bc68882efc26c49b21aeaf7796))
28
- * **downloader:** add native video downloader with queue system ([8c6e236](https://github.com/sebastian-software/offcourse/commit/8c6e23620682c72fd403eb4360931ce91c4fb5ec))
29
- * extract Vimeo URLs from running player in iframe ([ba68bbe](https://github.com/sebastian-software/offcourse/commit/ba68bbe2db305531bfcda0b8a10d4d845715603b))
30
- * format transcripts with paragraphs ([cd16eb8](https://github.com/sebastian-software/offcourse/commit/cd16eb8d3f0205802927a0623ad124a7407dff9b))
31
- * improve download progress display and file size reporting ([c5a548c](https://github.com/sebastian-software/offcourse/commit/c5a548c0c601c7c493a616eb44a982aee70ba56a))
32
- * improve locked lesson detection using hasAccess from JSON ([6b2e3e8](https://github.com/sebastian-software/offcourse/commit/6b2e3e80fe8a425e032dff8179389bb0963e920f))
33
- * improved logging for unsupported video providers ([f808169](https://github.com/sebastian-software/offcourse/commit/f808169ef88bda9006cf12955f3e0b154fdc9ae6))
34
- * parallel downloads for faster video syncing ([95d12f2](https://github.com/sebastian-software/offcourse/commit/95d12f2071281544523bd069980c1449c47acf7e))
35
- * progress bar for Phase 1 (course structure scanning) ([2017425](https://github.com/sebastian-software/offcourse/commit/201742583b4842ebe6f1d01a39c4280988e15ba2))
36
- * progress bars for validation and content extraction phases ([324be59](https://github.com/sebastian-software/offcourse/commit/324be597ed04e1d04f70e4c58139b2dc65648129))
37
- * remove AI transcription and enrich feature ([7ba6327](https://github.com/sebastian-software/offcourse/commit/7ba632753a5e08d67a653ff24452d83bd3406eb7))
38
- * **scraper:** add Playwright-based Skool scraper ([b9b111f](https://github.com/sebastian-software/offcourse/commit/b9b111f6e22ad03c78fe14d040caeee0d510a901))
39
- * separate summary.md and transcript.md, add module summaries ([fc49a60](https://github.com/sebastian-software/offcourse/commit/fc49a6097cf612ac3a63f6c060060febd0f6b8ed))
40
- * SQLite state management, improved video detection, graceful shutdown ([fc001d7](https://github.com/sebastian-software/offcourse/commit/fc001d7a82705b59ed4973d66d7ca8a0406c7dd4))
41
- * **storage:** add filesystem utilities for course output ([ef18545](https://github.com/sebastian-software/offcourse/commit/ef185453a9afc6d0ff373e8dd58ce812514d6b46))
42
- * support domain-restricted Vimeo videos via browser context ([2b16e0c](https://github.com/sebastian-software/offcourse/commit/2b16e0c21f483ecbac3b22a9e09b5f8835367d79))
43
- * use CDP network interception to capture video URLs from iframes ([b49a873](https://github.com/sebastian-software/offcourse/commit/b49a873a6a1f4db7285e673f80de85683f548332))
44
- * use readable titles in summary and transcript files ([ad1377d](https://github.com/sebastian-software/offcourse/commit/ad1377d83bb07611adca439546969fb9c7fdc5b7))
45
-
46
- ### Bug Fixes
47
-
48
- * --force flag now also resets error lessons for retry ([d20e7fb](https://github.com/sebastian-software/offcourse/commit/d20e7fb95ed98d7cd9d70d798baa888ce1cd60f5))
49
- * --resume --retry-errors now works correctly ([bbe732d](https://github.com/sebastian-software/offcourse/commit/bbe732d36e43a421ff3faa9e81f2ccfc35841fd3))
50
- * add autoplay=1 to embed URLs to trigger HLS fetch ([1ffa068](https://github.com/sebastian-software/offcourse/commit/1ffa068ece40553118d573b0b55ff14fecfb41f2))
51
- * add conventional-changelog-conventionalcommits peer dependency ([84f4502](https://github.com/sebastian-software/offcourse/commit/84f45029e5a81e76ab16a5f38beae16197701664))
52
- * add missing await to saveMarkdown call ([da6e1e7](https://github.com/sebastian-software/offcourse/commit/da6e1e7dc50de9441c7ff7fc2bb5f95ddbdbb3a9))
53
- * capture Loom/Vimeo HLS by navigating to embed page ([6608bd9](https://github.com/sebastian-software/offcourse/commit/6608bd9f9eee6107912af259340fb68ce86701e0))
54
- * clean download progress display - remove completed bars ([8334894](https://github.com/sebastian-software/offcourse/commit/83348940a44859dc3cd915c8415d473829f3d2d0))
55
- * extract full iframe URLs with auth params for Vimeo ([e8fcec3](https://github.com/sebastian-software/offcourse/commit/e8fcec308c3208ba2fd1a47b012926d77d199acf))
56
- * formatting ([4491c00](https://github.com/sebastian-software/offcourse/commit/4491c00ed610997b55314ed4d456e42325de5f3f))
57
- * handle direct HLS URLs in Loom downloader ([974b7d6](https://github.com/sebastian-software/offcourse/commit/974b7d6e7024f2f711abd3be815f3fee64bd4b10))
58
- * **highlevel:** correctly parse product API response for course name ([697ab07](https://github.com/sebastian-software/offcourse/commit/697ab07575e61e6cd85481743c79a2f1ca74cbfb))
59
- * **highlevel:** fix video detection and default to headless mode ([e4dcf51](https://github.com/sebastian-software/offcourse/commit/e4dcf513b0b81ddfdfec9539ae4fa8ea1f0bb0ed))
60
- * migrate to Zod 4 API for url and datetime validation ([7519b2c](https://github.com/sebastian-software/offcourse/commit/7519b2c3f5155a938d2ec55e8a5c7e70c371659b))
61
- * mute videos during extraction to avoid audio output ([9150dc0](https://github.com/sebastian-software/offcourse/commit/9150dc09cac1e7c70eb9a9dd257a9e4f6fd924b9))
62
- * mute videos during extraction to avoid audio output ([ee43c1c](https://github.com/sebastian-software/offcourse/commit/ee43c1c18c138aa014bdd913db871593adcd0dda))
63
- * **navigator:** correctly handle module URLs vs classroom URLs ([a0e8763](https://github.com/sebastian-software/offcourse/commit/a0e8763c982a65ba7a38d59c9571dbeee577ac3f))
64
- * prefix unused parameter with underscore in queue test ([076f9c4](https://github.com/sebastian-software/offcourse/commit/076f9c4f1906358075d4061c81b31aa1b579b8da))
65
- * resolve lint errors in syncHighLevel.ts ([4af50d5](https://github.com/sebastian-software/offcourse/commit/4af50d5615ab260de7778e2248cf3fb0bedda851))
66
- * simplify release-it preset config for v10 compatibility ([9f9748f](https://github.com/sebastian-software/offcourse/commit/9f9748f829f1a92b9c397d5ff4ab690ac044f8f7))
67
- * use Playwright request API for Vimeo (avoids CORS) ([09a2386](https://github.com/sebastian-software/offcourse/commit/09a23867115bded76521e27e0afbda76970b50d2))
68
- * use response listener instead of route interception ([cb63eee](https://github.com/sebastian-software/offcourse/commit/cb63eeeeb2d2e13f3d0cba22ed702b35c1b88525))
69
- * use URL slug for output directory name ([804e4fb](https://github.com/sebastian-software/offcourse/commit/804e4fb9d99615feccf6973b1fc603f02336146a))
70
- * version ([d17bbc7](https://github.com/sebastian-software/offcourse/commit/d17bbc7497e660634262af2d8047f96781ff4ac2))
71
-
72
- ### Performance Improvements
73
-
74
- * reduce wait times and improve content extraction ([e306be2](https://github.com/sebastian-software/offcourse/commit/e306be27d8d021a273801c0f5739639cab3599f7))
75
-
76
- All notable changes to this project will be documented in this file.
77
-
78
- This changelog is automatically generated based on [Conventional Commits](https://www.conventionalcommits.org/).
@@ -1,4 +0,0 @@
1
- export default {
2
- extends: ["@commitlint/config-conventional"],
3
- };
4
-
@@ -1,47 +0,0 @@
1
- export interface OpenRouterOptions {
2
- model?: string;
3
- maxTokens?: number;
4
- temperature?: number;
5
- }
6
- export interface ChatMessage {
7
- role: "system" | "user" | "assistant";
8
- content: string;
9
- }
10
- export interface UsageInfo {
11
- promptTokens: number;
12
- completionTokens: number;
13
- totalTokens: number;
14
- cost: number | undefined;
15
- }
16
- export interface ChatResult {
17
- content: string;
18
- usage: UsageInfo;
19
- model: string;
20
- }
21
- /**
22
- * Get cumulative usage stats for the current session.
23
- */
24
- export declare function getCumulativeUsage(): UsageInfo;
25
- /**
26
- * Reset cumulative usage stats.
27
- */
28
- export declare function resetCumulativeUsage(): void;
29
- /**
30
- * Check if OpenRouter is configured.
31
- */
32
- export declare function isConfigured(): boolean;
33
- /**
34
- * Send a chat completion request to OpenRouter.
35
- * Returns full result with usage info.
36
- */
37
- export declare function chatWithUsage(messages: ChatMessage[], options?: OpenRouterOptions): Promise<ChatResult>;
38
- /**
39
- * Send a chat completion request to OpenRouter.
40
- * Simple version that just returns the content string.
41
- */
42
- export declare function chat(messages: ChatMessage[], options?: OpenRouterOptions): Promise<string>;
43
- /**
44
- * Simple prompt helper.
45
- */
46
- export declare function prompt(userPrompt: string, systemPrompt?: string, options?: OpenRouterOptions): Promise<string>;
47
- //# sourceMappingURL=openRouter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"openRouter.d.ts","sourceRoot":"","sources":["../../src/ai/openRouter.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAUD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,CAE9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAO3C;AAsBD;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAcD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CA+CrB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CACxB,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,MAAM,CAC1B,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAUjB"}