coursecode 0.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 (362) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +322 -0
  3. package/THIRD_PARTY_NOTICES.md +22 -0
  4. package/bin/cli.js +331 -0
  5. package/framework/assets/logo-coursecode-black.svg +14 -0
  6. package/framework/assets/logo-coursecode-white.svg +14 -0
  7. package/framework/assets/logo-coursecode.svg +14 -0
  8. package/framework/css/01-base.css +160 -0
  9. package/framework/css/02-layout.css +499 -0
  10. package/framework/css/accessibility.css +834 -0
  11. package/framework/css/components/accordions.css +710 -0
  12. package/framework/css/components/assessments.css +520 -0
  13. package/framework/css/components/audio-player.css +570 -0
  14. package/framework/css/components/badges.css +80 -0
  15. package/framework/css/components/breadcrumbs.css +87 -0
  16. package/framework/css/components/buttons.css +707 -0
  17. package/framework/css/components/callouts.css +1280 -0
  18. package/framework/css/components/cards.css +475 -0
  19. package/framework/css/components/carousel.css +193 -0
  20. package/framework/css/components/checkbox-group.css +123 -0
  21. package/framework/css/components/checklist.css +203 -0
  22. package/framework/css/components/collapse.css +96 -0
  23. package/framework/css/components/comparison.css +33 -0
  24. package/framework/css/components/content-image.css +36 -0
  25. package/framework/css/components/document-gallery.css +425 -0
  26. package/framework/css/components/dropdown.css +115 -0
  27. package/framework/css/components/embed-frame.css +142 -0
  28. package/framework/css/components/engagement.css +412 -0
  29. package/framework/css/components/features.css +35 -0
  30. package/framework/css/components/flip-cards.css +253 -0
  31. package/framework/css/components/footer.css +353 -0
  32. package/framework/css/components/forms.css +294 -0
  33. package/framework/css/components/hero.css +216 -0
  34. package/framework/css/components/images.css +528 -0
  35. package/framework/css/components/interactive-timeline.css +274 -0
  36. package/framework/css/components/intro-cards.css +30 -0
  37. package/framework/css/components/lightbox.css +666 -0
  38. package/framework/css/components/loading.css +65 -0
  39. package/framework/css/components/modals.css +235 -0
  40. package/framework/css/components/notifications.css +107 -0
  41. package/framework/css/components/quote.css +150 -0
  42. package/framework/css/components/sidebar.css +684 -0
  43. package/framework/css/components/slide-header.css +52 -0
  44. package/framework/css/components/spinner.css +62 -0
  45. package/framework/css/components/stats.css +44 -0
  46. package/framework/css/components/steps.css +232 -0
  47. package/framework/css/components/tables.css +90 -0
  48. package/framework/css/components/tabs.css +347 -0
  49. package/framework/css/components/timeline.css +154 -0
  50. package/framework/css/components/toggle.css +95 -0
  51. package/framework/css/components/tooltip.css +226 -0
  52. package/framework/css/components/video-player.css +438 -0
  53. package/framework/css/design-tokens.css +707 -0
  54. package/framework/css/framework.css +86 -0
  55. package/framework/css/interactions/accessibility.css +75 -0
  56. package/framework/css/interactions/base.css +92 -0
  57. package/framework/css/interactions/drag-drop.css +295 -0
  58. package/framework/css/interactions/fill-in-the-blank.css +236 -0
  59. package/framework/css/interactions/hotspots.css +69 -0
  60. package/framework/css/interactions/index.css +45 -0
  61. package/framework/css/interactions/interactive-image.css +359 -0
  62. package/framework/css/interactions/likert.css +126 -0
  63. package/framework/css/interactions/matching.css +354 -0
  64. package/framework/css/interactions/numeric-input.css +78 -0
  65. package/framework/css/interactions/sequencing.css +378 -0
  66. package/framework/css/interactions/true-false.css +177 -0
  67. package/framework/css/layouts/article.css +258 -0
  68. package/framework/css/layouts/base.css +30 -0
  69. package/framework/css/layouts/canvas.css +38 -0
  70. package/framework/css/layouts/focused.css +236 -0
  71. package/framework/css/layouts/index.css +29 -0
  72. package/framework/css/layouts/presentation.css +191 -0
  73. package/framework/css/layouts/traditional.css +52 -0
  74. package/framework/css/responsive.css +439 -0
  75. package/framework/css/utilities/accessibility-utils.css +59 -0
  76. package/framework/css/utilities/animations.css +419 -0
  77. package/framework/css/utilities/borders.css +72 -0
  78. package/framework/css/utilities/colors.css +76 -0
  79. package/framework/css/utilities/container.css +46 -0
  80. package/framework/css/utilities/decorative.css +442 -0
  81. package/framework/css/utilities/display.css +257 -0
  82. package/framework/css/utilities/flexbox.css +80 -0
  83. package/framework/css/utilities/grid.css +69 -0
  84. package/framework/css/utilities/icons.css +534 -0
  85. package/framework/css/utilities/lists.css +190 -0
  86. package/framework/css/utilities/spacing.css +167 -0
  87. package/framework/css/utilities/tables.css +81 -0
  88. package/framework/css/utilities/typography.css +159 -0
  89. package/framework/css/utilities/visibility.css +117 -0
  90. package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
  91. package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
  92. package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
  93. package/framework/docs/DATA_MODEL.md +409 -0
  94. package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
  95. package/framework/docs/USER_GUIDE.md +583 -0
  96. package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
  97. package/framework/docs/examples/cloudflare-data-worker.js +102 -0
  98. package/framework/docs/examples/cloudflare-error-worker.js +228 -0
  99. package/framework/index.html +175 -0
  100. package/framework/js/app/AppActions.js +410 -0
  101. package/framework/js/app/AppState.js +225 -0
  102. package/framework/js/app/AppUI.js +616 -0
  103. package/framework/js/assessment/AssessmentActions.js +615 -0
  104. package/framework/js/assessment/AssessmentFactory.js +471 -0
  105. package/framework/js/assessment/AssessmentState.js +322 -0
  106. package/framework/js/assessment/AssessmentUI.js +451 -0
  107. package/framework/js/automation/api-engagement.js +196 -0
  108. package/framework/js/automation/api-interactions.js +167 -0
  109. package/framework/js/automation/api.js +242 -0
  110. package/framework/js/automation/index.js +41 -0
  111. package/framework/js/components/interactions/drag-drop.js +884 -0
  112. package/framework/js/components/interactions/fill-in.js +535 -0
  113. package/framework/js/components/interactions/hotspot.js +702 -0
  114. package/framework/js/components/interactions/interaction-base.js +511 -0
  115. package/framework/js/components/interactions/likert.js +301 -0
  116. package/framework/js/components/interactions/matching.js +699 -0
  117. package/framework/js/components/interactions/multiple-choice.js +377 -0
  118. package/framework/js/components/interactions/numeric.js +271 -0
  119. package/framework/js/components/interactions/sequencing.js +423 -0
  120. package/framework/js/components/interactions/true-false.js +241 -0
  121. package/framework/js/components/ui-components/accordion.js +442 -0
  122. package/framework/js/components/ui-components/alert.js +88 -0
  123. package/framework/js/components/ui-components/audio-player.js +1193 -0
  124. package/framework/js/components/ui-components/callout.js +121 -0
  125. package/framework/js/components/ui-components/carousel.js +145 -0
  126. package/framework/js/components/ui-components/checkbox-group.js +87 -0
  127. package/framework/js/components/ui-components/checklist.js +40 -0
  128. package/framework/js/components/ui-components/collapse.js +114 -0
  129. package/framework/js/components/ui-components/comparison.js +30 -0
  130. package/framework/js/components/ui-components/conditional-display.js +150 -0
  131. package/framework/js/components/ui-components/content-image.js +41 -0
  132. package/framework/js/components/ui-components/dropdown.js +262 -0
  133. package/framework/js/components/ui-components/embed-frame.js +274 -0
  134. package/framework/js/components/ui-components/features.js +33 -0
  135. package/framework/js/components/ui-components/flip-card.js +230 -0
  136. package/framework/js/components/ui-components/form-validator.js +76 -0
  137. package/framework/js/components/ui-components/hero.js +49 -0
  138. package/framework/js/components/ui-components/index.js +12 -0
  139. package/framework/js/components/ui-components/interactive-image.js +235 -0
  140. package/framework/js/components/ui-components/interactive-timeline.js +285 -0
  141. package/framework/js/components/ui-components/intro-cards.js +35 -0
  142. package/framework/js/components/ui-components/lightbox.js +652 -0
  143. package/framework/js/components/ui-components/modal.js +386 -0
  144. package/framework/js/components/ui-components/notifications.js +145 -0
  145. package/framework/js/components/ui-components/progress.js +88 -0
  146. package/framework/js/components/ui-components/quote.js +41 -0
  147. package/framework/js/components/ui-components/stats.js +33 -0
  148. package/framework/js/components/ui-components/steps.js +41 -0
  149. package/framework/js/components/ui-components/tabs.js +255 -0
  150. package/framework/js/components/ui-components/timeline.js +42 -0
  151. package/framework/js/components/ui-components/toggle-group.js +73 -0
  152. package/framework/js/components/ui-components/tooltip.js +458 -0
  153. package/framework/js/components/ui-components/value-display.js +133 -0
  154. package/framework/js/components/ui-components/video-player.js +686 -0
  155. package/framework/js/core/component-catalog.js +121 -0
  156. package/framework/js/core/event-bus.js +178 -0
  157. package/framework/js/core/interaction-catalog.js +149 -0
  158. package/framework/js/dev/runtime-linter.js +1725 -0
  159. package/framework/js/drivers/cmi5-driver.js +768 -0
  160. package/framework/js/drivers/driver-factory.js +77 -0
  161. package/framework/js/drivers/driver-interface.js +110 -0
  162. package/framework/js/drivers/http-driver-base.js +241 -0
  163. package/framework/js/drivers/lti-driver.js +508 -0
  164. package/framework/js/drivers/proxy-driver.js +444 -0
  165. package/framework/js/drivers/scorm-12-driver.js +560 -0
  166. package/framework/js/drivers/scorm-2004-driver.js +775 -0
  167. package/framework/js/drivers/scorm-driver-base.js +112 -0
  168. package/framework/js/engagement/engagement-manager.js +404 -0
  169. package/framework/js/engagement/engagement-progress.js +191 -0
  170. package/framework/js/engagement/engagement-trackers.js +215 -0
  171. package/framework/js/engagement/requirement-strategies.js +268 -0
  172. package/framework/js/main.js +727 -0
  173. package/framework/js/managers/accessibility-manager.js +499 -0
  174. package/framework/js/managers/assessment-manager.js +230 -0
  175. package/framework/js/managers/audio-manager.js +944 -0
  176. package/framework/js/managers/comment-manager.js +88 -0
  177. package/framework/js/managers/flag-manager.js +86 -0
  178. package/framework/js/managers/interaction-manager.js +254 -0
  179. package/framework/js/managers/interaction-registry.js +96 -0
  180. package/framework/js/managers/objective-manager.js +423 -0
  181. package/framework/js/managers/score-manager.js +441 -0
  182. package/framework/js/managers/video-manager.js +536 -0
  183. package/framework/js/navigation/Breadcrumbs.js +234 -0
  184. package/framework/js/navigation/NavigationActions.js +1132 -0
  185. package/framework/js/navigation/NavigationState.js +276 -0
  186. package/framework/js/navigation/NavigationUI.js +574 -0
  187. package/framework/js/navigation/document-gallery.js +357 -0
  188. package/framework/js/navigation/navigation-helpers.js +175 -0
  189. package/framework/js/navigation/navigation-validators.js +174 -0
  190. package/framework/js/state/index.js +8 -0
  191. package/framework/js/state/lms-connection.js +482 -0
  192. package/framework/js/state/lms-error-utils.js +58 -0
  193. package/framework/js/state/state-commits.js +200 -0
  194. package/framework/js/state/state-domains.js +86 -0
  195. package/framework/js/state/state-manager.js +502 -0
  196. package/framework/js/state/state-validation.js +311 -0
  197. package/framework/js/state/transaction-log.js +41 -0
  198. package/framework/js/state/xapi-statement-service.js +325 -0
  199. package/framework/js/utilities/access-control.js +99 -0
  200. package/framework/js/utilities/breakpoint-manager.js +315 -0
  201. package/framework/js/utilities/canvas-slide.js +35 -0
  202. package/framework/js/utilities/conditional-display.js +388 -0
  203. package/framework/js/utilities/course-channel.js +214 -0
  204. package/framework/js/utilities/course-helpers.js +420 -0
  205. package/framework/js/utilities/data-reporter.js +273 -0
  206. package/framework/js/utilities/error-reporter.js +313 -0
  207. package/framework/js/utilities/hotspot-helper.js +341 -0
  208. package/framework/js/utilities/icons.js +348 -0
  209. package/framework/js/utilities/logger.js +92 -0
  210. package/framework/js/utilities/markdown-renderer.js +45 -0
  211. package/framework/js/utilities/scroll-tracker.js +68 -0
  212. package/framework/js/utilities/ui-initializer.js +146 -0
  213. package/framework/js/utilities/utilities.js +293 -0
  214. package/framework/js/utilities/view-manager.js +227 -0
  215. package/framework/js/validation/html-validators.js +422 -0
  216. package/framework/js/validation/scorm-validators.js +438 -0
  217. package/framework/js/vendor/pipwerks.js +931 -0
  218. package/framework/scripts/generate-narration.js +629 -0
  219. package/framework/scripts/tts-providers/azure-provider.js +178 -0
  220. package/framework/scripts/tts-providers/base-provider.js +81 -0
  221. package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
  222. package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
  223. package/framework/scripts/tts-providers/google-provider.js +272 -0
  224. package/framework/scripts/tts-providers/index.js +158 -0
  225. package/framework/scripts/tts-providers/openai-provider.js +143 -0
  226. package/framework/version.json +63 -0
  227. package/lib/authoring-api.js +919 -0
  228. package/lib/build-linter.js +450 -0
  229. package/lib/build-packaging.js +186 -0
  230. package/lib/build.js +88 -0
  231. package/lib/cloud.js +691 -0
  232. package/lib/convert.js +341 -0
  233. package/lib/course-parser.js +936 -0
  234. package/lib/course-writer.js +258 -0
  235. package/lib/create.js +248 -0
  236. package/lib/css-index.js +237 -0
  237. package/lib/dev.js +51 -0
  238. package/lib/export-content.js +1246 -0
  239. package/lib/headless-browser.js +413 -0
  240. package/lib/import.js +377 -0
  241. package/lib/index.js +80 -0
  242. package/lib/info.js +79 -0
  243. package/lib/interaction-formatters.js +568 -0
  244. package/lib/manifest/cmi5-manifest.js +63 -0
  245. package/lib/manifest/lti-tool-config.js +53 -0
  246. package/lib/manifest/manifest-factory.js +99 -0
  247. package/lib/manifest/scorm-12-manifest.js +61 -0
  248. package/lib/manifest/scorm-2004-manifest.js +94 -0
  249. package/lib/manifest/scorm-proxy-manifest.js +104 -0
  250. package/lib/manifest-parser.js +96 -0
  251. package/lib/mcp-prompts.js +753 -0
  252. package/lib/mcp-server.js +316 -0
  253. package/lib/narration.js +53 -0
  254. package/lib/pdf-structure.js +142 -0
  255. package/lib/preview-export.js +231 -0
  256. package/lib/preview-routes-api.js +662 -0
  257. package/lib/preview-routes-editing.js +159 -0
  258. package/lib/preview-routes-lms.js +230 -0
  259. package/lib/preview-server.js +564 -0
  260. package/lib/project-utils.js +269 -0
  261. package/lib/proxy-templates/proxy.html +68 -0
  262. package/lib/proxy-templates/scorm-bridge.js +112 -0
  263. package/lib/scaffold.js +193 -0
  264. package/lib/schema-extractor.js +361 -0
  265. package/lib/slide-source-editor.js +586 -0
  266. package/lib/stub-player/app-viewer.js +195 -0
  267. package/lib/stub-player/app.js +370 -0
  268. package/lib/stub-player/catalog-panel.js +312 -0
  269. package/lib/stub-player/config-panel.js +1303 -0
  270. package/lib/stub-player/content-generator.js +586 -0
  271. package/lib/stub-player/content-viewer.js +173 -0
  272. package/lib/stub-player/debug-panel.js +420 -0
  273. package/lib/stub-player/edit-mode.js +922 -0
  274. package/lib/stub-player/edit-utils.js +400 -0
  275. package/lib/stub-player/header-bar.js +354 -0
  276. package/lib/stub-player/interaction-editor.js +210 -0
  277. package/lib/stub-player/interactions-panel.js +565 -0
  278. package/lib/stub-player/lms-api.js +1094 -0
  279. package/lib/stub-player/login-screen.js +74 -0
  280. package/lib/stub-player/outline-mode.js +689 -0
  281. package/lib/stub-player/styles/_assessments-panel.css +245 -0
  282. package/lib/stub-player/styles/_base.css +89 -0
  283. package/lib/stub-player/styles/_catalog-icons.css +96 -0
  284. package/lib/stub-player/styles/_catalog-panel.css +291 -0
  285. package/lib/stub-player/styles/_config-panel.css +636 -0
  286. package/lib/stub-player/styles/_content-viewer.css +834 -0
  287. package/lib/stub-player/styles/_debug-panel.css +576 -0
  288. package/lib/stub-player/styles/_edit-mode.css +128 -0
  289. package/lib/stub-player/styles/_header-bar.css +343 -0
  290. package/lib/stub-player/styles/_interaction-editor.css +140 -0
  291. package/lib/stub-player/styles/_interactions-panel.css +1038 -0
  292. package/lib/stub-player/styles/_login-screen.css +102 -0
  293. package/lib/stub-player/styles/_outline-mode.css +752 -0
  294. package/lib/stub-player/styles.css +15 -0
  295. package/lib/stub-player.js +160 -0
  296. package/lib/test-data-reporting.js +176 -0
  297. package/lib/test-error-reporting.js +146 -0
  298. package/lib/token.js +86 -0
  299. package/lib/upgrade.js +257 -0
  300. package/lib/validation-rules.js +517 -0
  301. package/lib/vite-plugin-content-discovery.js +296 -0
  302. package/package.json +108 -0
  303. package/schemas/XMLSchema.dtd +402 -0
  304. package/schemas/adlcp_v1p3.xsd +111 -0
  305. package/schemas/adlnav_v1p3.xsd +61 -0
  306. package/schemas/adlseq_v1p3.xsd +93 -0
  307. package/schemas/common/anyElement.xsd +27 -0
  308. package/schemas/common/dataTypes.xsd +138 -0
  309. package/schemas/common/elementNames.xsd +767 -0
  310. package/schemas/common/elementTypes.xsd +786 -0
  311. package/schemas/common/rootElement.xsd +31 -0
  312. package/schemas/common/vocabTypes.xsd +345 -0
  313. package/schemas/common/vocabValues.xsd +257 -0
  314. package/schemas/datatypes.dtd +203 -0
  315. package/schemas/ims_xml.xsd +35 -0
  316. package/schemas/imscp_v1p1.xsd +368 -0
  317. package/schemas/imsss_v1p0.xsd +67 -0
  318. package/schemas/imsss_v1p0auxresource.xsd +19 -0
  319. package/schemas/imsss_v1p0control.xsd +20 -0
  320. package/schemas/imsss_v1p0delivery.xsd +17 -0
  321. package/schemas/imsss_v1p0limit.xsd +47 -0
  322. package/schemas/imsss_v1p0objective.xsd +67 -0
  323. package/schemas/imsss_v1p0random.xsd +16 -0
  324. package/schemas/imsss_v1p0rollup.xsd +46 -0
  325. package/schemas/imsss_v1p0seqrule.xsd +108 -0
  326. package/schemas/imsss_v1p0util.xsd +94 -0
  327. package/schemas/license.txt +17 -0
  328. package/schemas/lom.xsd +102 -0
  329. package/schemas/lomCustom.xsd +62 -0
  330. package/schemas/lomLoose.xsd +62 -0
  331. package/schemas/lomStrict.xsd +62 -0
  332. package/schemas/xml.xsd +81 -0
  333. package/template/.env.example +92 -0
  334. package/template/course/assets/audio/example-intro.mp3 +0 -0
  335. package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
  336. package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
  337. package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
  338. package/template/course/assets/docs/example_md_1.md +39 -0
  339. package/template/course/assets/docs/example_md_2.md +41 -0
  340. package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
  341. package/template/course/assets/docs/example_pdf_2.pdf +0 -0
  342. package/template/course/assets/images/course-architecture.svg +36 -0
  343. package/template/course/assets/images/logo.svg +14 -0
  344. package/template/course/assets/widgets/counter-demo.html +190 -0
  345. package/template/course/assets/widgets/gravity-painter.html +384 -0
  346. package/template/course/course-config.js +539 -0
  347. package/template/course/icons.js +19 -0
  348. package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
  349. package/template/course/slides/example-course-structure.js +138 -0
  350. package/template/course/slides/example-final-exam.js +144 -0
  351. package/template/course/slides/example-finishing.js +127 -0
  352. package/template/course/slides/example-interactions-showcase.js +615 -0
  353. package/template/course/slides/example-preview-tour.js +129 -0
  354. package/template/course/slides/example-remedial.js +143 -0
  355. package/template/course/slides/example-summary.js +103 -0
  356. package/template/course/slides/example-ui-showcase.js +1805 -0
  357. package/template/course/slides/example-welcome.js +123 -0
  358. package/template/course/slides/example-workflow.js +140 -0
  359. package/template/course/theme.css +165 -0
  360. package/template/eslint.config.js +47 -0
  361. package/template/package.json +28 -0
  362. package/template/vite.config.js +339 -0
@@ -0,0 +1,339 @@
1
+ import { defineConfig } from 'vite';
2
+ import legacy from '@vitejs/plugin-legacy';
3
+ import { viteStaticCopy } from 'vite-plugin-static-copy';
4
+ import path from 'path';
5
+ import { fileURLToPath, pathToFileURL } from 'url';
6
+ import fs from 'fs';
7
+
8
+ let generateManifest;
9
+ let contentDiscoveryPlugin;
10
+ let createStandardPackage;
11
+ let createExternalPackagesForClients;
12
+ let validateExternalHostingConfig;
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+ const ROOT_DIR = path.resolve(__dirname);
16
+ const DIST_DIR = path.join(ROOT_DIR, 'dist');
17
+
18
+ async function loadBuildUtils() {
19
+ if (
20
+ generateManifest &&
21
+ contentDiscoveryPlugin &&
22
+ createStandardPackage &&
23
+ createExternalPackagesForClients &&
24
+ validateExternalHostingConfig
25
+ ) {
26
+ return;
27
+ }
28
+
29
+ try {
30
+ ({ generateManifest } = await import('coursecode/manifest'));
31
+ ({
32
+ createStandardPackage,
33
+ createExternalPackagesForClients,
34
+ validateExternalHostingConfig
35
+ } = await import('coursecode/build-packaging'));
36
+ ({ default: contentDiscoveryPlugin } = await import('coursecode/vite-plugin-content-discovery'));
37
+ } catch {
38
+ // Fallback for npm link / local dev — preview-server provides COURSECODE_LIB_DIR
39
+ const libDir = process.env.COURSECODE_LIB_DIR;
40
+ if (!libDir) throw new Error('Cannot resolve coursecode package. Run: npm install coursecode');
41
+ const toUrl = (f) => pathToFileURL(path.join(libDir, f)).href;
42
+ ({ generateManifest } = await import(toUrl('manifest/manifest-factory.js')));
43
+ ({
44
+ createStandardPackage,
45
+ createExternalPackagesForClients,
46
+ validateExternalHostingConfig
47
+ } = await import(toUrl('build-packaging.js')));
48
+ ({ default: contentDiscoveryPlugin } = await import(toUrl('vite-plugin-content-discovery.js')));
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Validate package structure based on format.
54
+ * @param {string} format - 'scorm2004' | 'scorm1.2' | 'cmi5' | 'lti'
55
+ */
56
+ function validatePackage(format = 'scorm2004') {
57
+ const errors = [];
58
+ const warnings = [];
59
+
60
+ // Get required files based on format
61
+ let requiredFiles;
62
+ if (format === 'cmi5') {
63
+ requiredFiles = ['cmi5.xml', 'index.html'];
64
+ } else if (format === 'lti') {
65
+ requiredFiles = ['lti-tool-config.json', 'index.html'];
66
+ } else if (format === 'scorm1.2') {
67
+ requiredFiles = ['imsmanifest.xml', 'index.html'];
68
+ // SCORM 1.2 schemas are optional for most LMSs
69
+ } else {
70
+ // SCORM 2004 default
71
+ requiredFiles = [
72
+ 'imsmanifest.xml',
73
+ 'index.html',
74
+ 'imscp_v1p1.xsd',
75
+ 'adlcp_v1p3.xsd',
76
+ 'imsss_v1p0.xsd'
77
+ ];
78
+ }
79
+
80
+ requiredFiles.forEach(file => {
81
+ const filePath = path.join(DIST_DIR, file);
82
+ if (!fs.existsSync(filePath)) {
83
+ errors.push(`Missing required file: ${file}`);
84
+ }
85
+ });
86
+
87
+ const assetsDir = path.join(DIST_DIR, 'assets');
88
+ if (!fs.existsSync(assetsDir)) {
89
+ warnings.push('No assets directory found');
90
+ }
91
+
92
+ // Validate manifest/tool config content by format
93
+ if (format === 'cmi5') {
94
+ // cmi5: Validate cmi5.xml structure
95
+ const cmi5Path = path.join(DIST_DIR, 'cmi5.xml');
96
+ if (fs.existsSync(cmi5Path)) {
97
+ const cmi5Content = fs.readFileSync(cmi5Path, 'utf-8');
98
+ if (!cmi5Content.includes('<courseStructure')) {
99
+ errors.push('Generated cmi5.xml missing root <courseStructure> element');
100
+ }
101
+ }
102
+ } else if (format === 'lti') {
103
+ const ltiPath = path.join(DIST_DIR, 'lti-tool-config.json');
104
+ if (fs.existsSync(ltiPath)) {
105
+ try {
106
+ const toolConfig = JSON.parse(fs.readFileSync(ltiPath, 'utf-8'));
107
+ if (!toolConfig.title || !toolConfig.target_link_uri) {
108
+ errors.push('Generated lti-tool-config.json missing required fields (title, target_link_uri)');
109
+ }
110
+ } catch {
111
+ errors.push('Generated lti-tool-config.json is not valid JSON');
112
+ }
113
+ }
114
+ } else {
115
+ const manifestPath = path.join(DIST_DIR, 'imsmanifest.xml');
116
+ if (fs.existsSync(manifestPath)) {
117
+ const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
118
+
119
+ if (!manifestContent.includes('<?xml version="1.0"')) {
120
+ errors.push('Generated manifest missing XML declaration');
121
+ }
122
+ if (!manifestContent.includes('<manifest')) {
123
+ errors.push('Generated manifest missing root <manifest> element');
124
+ }
125
+ }
126
+ }
127
+
128
+ return { errors, warnings };
129
+ }
130
+
131
+ /**
132
+ * Load course-config.js using module import to support nested structures.
133
+ */
134
+ async function loadCourseConfig() {
135
+ const configPath = path.join(ROOT_DIR, 'course', 'course-config.js');
136
+ if (!fs.existsSync(configPath)) {
137
+ throw new Error('course-config.js not found');
138
+ }
139
+
140
+ const configUrl = `${pathToFileURL(configPath).href}?t=${Date.now()}`;
141
+ const module = await import(configUrl);
142
+ const config = module.courseConfig || module.default || {};
143
+
144
+ return {
145
+ title: config.metadata?.title || 'SCORM Course',
146
+ description: config.metadata?.description || 'SCORM 2004 4th Edition Course',
147
+ version: config.metadata?.version || '1.0.0',
148
+ author: config.metadata?.author || 'Unknown',
149
+ language: config.metadata?.language || 'en',
150
+ windowWidth: config.environment?.window?.width || 1024,
151
+ windowHeight: config.environment?.window?.height || 768,
152
+ // LMS format: CLI override (env var) takes precedence over config file
153
+ lmsFormat: process.env.LMS_FORMAT || config.format || 'cmi5',
154
+ externalUrl: config.externalUrl || null,
155
+ accessControl: config.accessControl || null,
156
+ galleryConfig: config.navigation?.documentGallery || null
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Scan dist directory for all files
162
+ */
163
+ function scanDistFiles() {
164
+ const files = [];
165
+
166
+ function scanDir(dir, relativePath = '') {
167
+ if (!fs.existsSync(dir)) return;
168
+
169
+ const items = fs.readdirSync(dir);
170
+ for (const item of items) {
171
+ const fullPath = path.join(dir, item);
172
+ const relPath = relativePath ? `${relativePath}/${item}` : item;
173
+
174
+ if (fs.statSync(fullPath).isDirectory()) {
175
+ scanDir(fullPath, relPath);
176
+ } else {
177
+ files.push(relPath);
178
+ }
179
+ }
180
+ }
181
+
182
+ scanDir(DIST_DIR);
183
+ return files;
184
+ }
185
+
186
+ /**
187
+ * SCORM post-build plugin
188
+ */
189
+ function scormPostBuild(isDev) {
190
+ return {
191
+ name: 'scorm-post-build',
192
+ buildStart() {
193
+ if (isDev) console.log('šŸ”Ø Building...');
194
+ },
195
+ closeBundle: async () => {
196
+ try {
197
+ // Move index.html to root
198
+ const indexSource = path.join(DIST_DIR, 'framework', 'index.html');
199
+ const indexDest = path.join(DIST_DIR, 'index.html');
200
+
201
+ // Load course config once for meta tag injection and manifest generation
202
+ const config = await loadCourseConfig();
203
+
204
+ if (fs.existsSync(indexSource)) {
205
+ fs.renameSync(indexSource, indexDest);
206
+
207
+ let indexContent = fs.readFileSync(indexDest, 'utf-8');
208
+ indexContent = indexContent.replace(/(src|href)="\.\.\/(assets|course)\//g, '$1="./$2/');
209
+
210
+ // Stamp LMS format meta tag for runtime driver selection
211
+ indexContent = indexContent.replace(
212
+ '<meta charset="UTF-8" />',
213
+ `<meta charset="UTF-8" />\n <meta name="lms-format" content="${config.lmsFormat}" />`
214
+ );
215
+
216
+ fs.writeFileSync(indexDest, indexContent, 'utf-8');
217
+
218
+ const frameworkDir = path.join(DIST_DIR, 'framework');
219
+ if (fs.existsSync(frameworkDir) && fs.readdirSync(frameworkDir).length === 0) {
220
+ fs.rmdirSync(frameworkDir);
221
+ }
222
+ }
223
+
224
+ // Generate manifest using factory
225
+ const files = isDev ? ['index.html'] : scanDistFiles();
226
+ const manifestOptions = { externalUrl: config.externalUrl };
227
+ const { filename, content } = generateManifest(config.lmsFormat, config, files, manifestOptions);
228
+ fs.writeFileSync(path.join(DIST_DIR, filename), content, 'utf-8');
229
+
230
+ if (!isDev) {
231
+ console.log(`āœ“ Generated ${filename}`);
232
+
233
+ const isProxyFormat = config.lmsFormat.endsWith('-proxy');
234
+ const isRemoteFormat = config.lmsFormat.endsWith('-remote');
235
+ const isExternalFormat = isProxyFormat || isRemoteFormat;
236
+
237
+ if (!isExternalFormat) {
238
+ const validationResults = validatePackage(config.lmsFormat);
239
+ if (validationResults.errors.length > 0) {
240
+ console.error('\nāœ— Validation failed:');
241
+ validationResults.errors.forEach(err => console.error(` - ${err}`));
242
+ throw new Error('Package validation failed');
243
+ }
244
+
245
+ console.log('āœ“ Package structure valid');
246
+ console.log('\nšŸ“¦ Creating package archive...');
247
+ await createStandardPackage({ rootDir: ROOT_DIR, distDir: DIST_DIR, config });
248
+ } else {
249
+ validateExternalHostingConfig(config);
250
+ console.log('\nšŸ“¦ Creating external hosting package archives...');
251
+ await createExternalPackagesForClients({ rootDir: ROOT_DIR, config });
252
+ }
253
+ console.log('\nāœ… Package built successfully');
254
+ } else {
255
+ console.log('āœ… Build complete\n');
256
+ }
257
+ } catch (error) {
258
+ console.error('āŒ Post-processing failed:', error.message);
259
+ throw error;
260
+ }
261
+ }
262
+ };
263
+ }
264
+
265
+ export default defineConfig(async ({ mode }) => {
266
+ await loadBuildUtils();
267
+ const isDev = mode === 'development';
268
+ const courseConfig = await loadCourseConfig();
269
+
270
+ return {
271
+ root: '.',
272
+ base: './',
273
+
274
+ define: {
275
+ // Build timestamp for cache-busting static assets (audio, images)
276
+ __BUILD_TIMESTAMP__: JSON.stringify(Date.now().toString()),
277
+ // Inject LMS format for driver selection at runtime
278
+ 'import.meta.env.LMS_FORMAT': JSON.stringify(courseConfig.lmsFormat),
279
+ // Dev mode flag for tree-shaking dev-only code (CourseLinter, etc.)
280
+ __DEV__: isDev
281
+ },
282
+
283
+ resolve: {
284
+ alias: {
285
+ '@slides': path.resolve(__dirname, 'course/slides'),
286
+ // COURSECODE_LIB_DIR is set by preview-server; falls back to installed package path
287
+ '@lib': process.env.COURSECODE_LIB_DIR || path.resolve(__dirname, 'node_modules/coursecode/lib')
288
+ }
289
+ },
290
+
291
+ build: {
292
+ outDir: 'dist',
293
+ emptyOutDir: !isDev,
294
+ manifest: true,
295
+ // LMS environments (SCORM/cmi5) can be restrictive with dynamic imports,
296
+ // so we intentionally keep main.js as a single chunk
297
+ chunkSizeWarningLimit: 600,
298
+ rollupOptions: {
299
+ input: {
300
+ main: path.resolve(__dirname, 'framework/index.html')
301
+ },
302
+ // All drivers are bundled as lazy chunks in the universal build.
303
+ // jose is dynamically imported by lti-driver.js only when LTI format
304
+ // is selected at runtime, so it's safe to include in all builds.
305
+ output: {
306
+ entryFileNames: 'assets/[name].js',
307
+ chunkFileNames: 'assets/[name].js',
308
+ assetFileNames: 'assets/[name].[ext]'
309
+ }
310
+ }
311
+ },
312
+
313
+ plugins: [
314
+ // Content discovery - generates manifest at build time
315
+ contentDiscoveryPlugin({ galleryConfig: courseConfig.galleryConfig }),
316
+
317
+ ...(!isDev ? [
318
+ legacy({
319
+ targets: ['ie >= 11'],
320
+ renderLegacyChunks: true,
321
+ modernPolyfills: true,
322
+ ignoreBrowserslistConfig: true
323
+ })
324
+ ] : []),
325
+
326
+ viteStaticCopy({
327
+ targets: [
328
+ { src: 'schemas/*.{xml,xsd,dtd}', dest: '.' },
329
+ { src: 'schemas/common/*', dest: 'common' },
330
+ { src: 'course', dest: '.' },
331
+ { src: 'framework/js/vendor/**/*', dest: 'js/vendor' }
332
+ ],
333
+ watch: isDev ? { rerun: true } : undefined
334
+ }),
335
+
336
+ scormPostBuild(isDev)
337
+ ]
338
+ };
339
+ });