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.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/bin/cli.js +331 -0
- package/framework/assets/logo-coursecode-black.svg +14 -0
- package/framework/assets/logo-coursecode-white.svg +14 -0
- package/framework/assets/logo-coursecode.svg +14 -0
- package/framework/css/01-base.css +160 -0
- package/framework/css/02-layout.css +499 -0
- package/framework/css/accessibility.css +834 -0
- package/framework/css/components/accordions.css +710 -0
- package/framework/css/components/assessments.css +520 -0
- package/framework/css/components/audio-player.css +570 -0
- package/framework/css/components/badges.css +80 -0
- package/framework/css/components/breadcrumbs.css +87 -0
- package/framework/css/components/buttons.css +707 -0
- package/framework/css/components/callouts.css +1280 -0
- package/framework/css/components/cards.css +475 -0
- package/framework/css/components/carousel.css +193 -0
- package/framework/css/components/checkbox-group.css +123 -0
- package/framework/css/components/checklist.css +203 -0
- package/framework/css/components/collapse.css +96 -0
- package/framework/css/components/comparison.css +33 -0
- package/framework/css/components/content-image.css +36 -0
- package/framework/css/components/document-gallery.css +425 -0
- package/framework/css/components/dropdown.css +115 -0
- package/framework/css/components/embed-frame.css +142 -0
- package/framework/css/components/engagement.css +412 -0
- package/framework/css/components/features.css +35 -0
- package/framework/css/components/flip-cards.css +253 -0
- package/framework/css/components/footer.css +353 -0
- package/framework/css/components/forms.css +294 -0
- package/framework/css/components/hero.css +216 -0
- package/framework/css/components/images.css +528 -0
- package/framework/css/components/interactive-timeline.css +274 -0
- package/framework/css/components/intro-cards.css +30 -0
- package/framework/css/components/lightbox.css +666 -0
- package/framework/css/components/loading.css +65 -0
- package/framework/css/components/modals.css +235 -0
- package/framework/css/components/notifications.css +107 -0
- package/framework/css/components/quote.css +150 -0
- package/framework/css/components/sidebar.css +684 -0
- package/framework/css/components/slide-header.css +52 -0
- package/framework/css/components/spinner.css +62 -0
- package/framework/css/components/stats.css +44 -0
- package/framework/css/components/steps.css +232 -0
- package/framework/css/components/tables.css +90 -0
- package/framework/css/components/tabs.css +347 -0
- package/framework/css/components/timeline.css +154 -0
- package/framework/css/components/toggle.css +95 -0
- package/framework/css/components/tooltip.css +226 -0
- package/framework/css/components/video-player.css +438 -0
- package/framework/css/design-tokens.css +707 -0
- package/framework/css/framework.css +86 -0
- package/framework/css/interactions/accessibility.css +75 -0
- package/framework/css/interactions/base.css +92 -0
- package/framework/css/interactions/drag-drop.css +295 -0
- package/framework/css/interactions/fill-in-the-blank.css +236 -0
- package/framework/css/interactions/hotspots.css +69 -0
- package/framework/css/interactions/index.css +45 -0
- package/framework/css/interactions/interactive-image.css +359 -0
- package/framework/css/interactions/likert.css +126 -0
- package/framework/css/interactions/matching.css +354 -0
- package/framework/css/interactions/numeric-input.css +78 -0
- package/framework/css/interactions/sequencing.css +378 -0
- package/framework/css/interactions/true-false.css +177 -0
- package/framework/css/layouts/article.css +258 -0
- package/framework/css/layouts/base.css +30 -0
- package/framework/css/layouts/canvas.css +38 -0
- package/framework/css/layouts/focused.css +236 -0
- package/framework/css/layouts/index.css +29 -0
- package/framework/css/layouts/presentation.css +191 -0
- package/framework/css/layouts/traditional.css +52 -0
- package/framework/css/responsive.css +439 -0
- package/framework/css/utilities/accessibility-utils.css +59 -0
- package/framework/css/utilities/animations.css +419 -0
- package/framework/css/utilities/borders.css +72 -0
- package/framework/css/utilities/colors.css +76 -0
- package/framework/css/utilities/container.css +46 -0
- package/framework/css/utilities/decorative.css +442 -0
- package/framework/css/utilities/display.css +257 -0
- package/framework/css/utilities/flexbox.css +80 -0
- package/framework/css/utilities/grid.css +69 -0
- package/framework/css/utilities/icons.css +534 -0
- package/framework/css/utilities/lists.css +190 -0
- package/framework/css/utilities/spacing.css +167 -0
- package/framework/css/utilities/tables.css +81 -0
- package/framework/css/utilities/typography.css +159 -0
- package/framework/css/utilities/visibility.css +117 -0
- package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
- package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
- package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
- package/framework/docs/DATA_MODEL.md +409 -0
- package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
- package/framework/docs/USER_GUIDE.md +583 -0
- package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
- package/framework/docs/examples/cloudflare-data-worker.js +102 -0
- package/framework/docs/examples/cloudflare-error-worker.js +228 -0
- package/framework/index.html +175 -0
- package/framework/js/app/AppActions.js +410 -0
- package/framework/js/app/AppState.js +225 -0
- package/framework/js/app/AppUI.js +616 -0
- package/framework/js/assessment/AssessmentActions.js +615 -0
- package/framework/js/assessment/AssessmentFactory.js +471 -0
- package/framework/js/assessment/AssessmentState.js +322 -0
- package/framework/js/assessment/AssessmentUI.js +451 -0
- package/framework/js/automation/api-engagement.js +196 -0
- package/framework/js/automation/api-interactions.js +167 -0
- package/framework/js/automation/api.js +242 -0
- package/framework/js/automation/index.js +41 -0
- package/framework/js/components/interactions/drag-drop.js +884 -0
- package/framework/js/components/interactions/fill-in.js +535 -0
- package/framework/js/components/interactions/hotspot.js +702 -0
- package/framework/js/components/interactions/interaction-base.js +511 -0
- package/framework/js/components/interactions/likert.js +301 -0
- package/framework/js/components/interactions/matching.js +699 -0
- package/framework/js/components/interactions/multiple-choice.js +377 -0
- package/framework/js/components/interactions/numeric.js +271 -0
- package/framework/js/components/interactions/sequencing.js +423 -0
- package/framework/js/components/interactions/true-false.js +241 -0
- package/framework/js/components/ui-components/accordion.js +442 -0
- package/framework/js/components/ui-components/alert.js +88 -0
- package/framework/js/components/ui-components/audio-player.js +1193 -0
- package/framework/js/components/ui-components/callout.js +121 -0
- package/framework/js/components/ui-components/carousel.js +145 -0
- package/framework/js/components/ui-components/checkbox-group.js +87 -0
- package/framework/js/components/ui-components/checklist.js +40 -0
- package/framework/js/components/ui-components/collapse.js +114 -0
- package/framework/js/components/ui-components/comparison.js +30 -0
- package/framework/js/components/ui-components/conditional-display.js +150 -0
- package/framework/js/components/ui-components/content-image.js +41 -0
- package/framework/js/components/ui-components/dropdown.js +262 -0
- package/framework/js/components/ui-components/embed-frame.js +274 -0
- package/framework/js/components/ui-components/features.js +33 -0
- package/framework/js/components/ui-components/flip-card.js +230 -0
- package/framework/js/components/ui-components/form-validator.js +76 -0
- package/framework/js/components/ui-components/hero.js +49 -0
- package/framework/js/components/ui-components/index.js +12 -0
- package/framework/js/components/ui-components/interactive-image.js +235 -0
- package/framework/js/components/ui-components/interactive-timeline.js +285 -0
- package/framework/js/components/ui-components/intro-cards.js +35 -0
- package/framework/js/components/ui-components/lightbox.js +652 -0
- package/framework/js/components/ui-components/modal.js +386 -0
- package/framework/js/components/ui-components/notifications.js +145 -0
- package/framework/js/components/ui-components/progress.js +88 -0
- package/framework/js/components/ui-components/quote.js +41 -0
- package/framework/js/components/ui-components/stats.js +33 -0
- package/framework/js/components/ui-components/steps.js +41 -0
- package/framework/js/components/ui-components/tabs.js +255 -0
- package/framework/js/components/ui-components/timeline.js +42 -0
- package/framework/js/components/ui-components/toggle-group.js +73 -0
- package/framework/js/components/ui-components/tooltip.js +458 -0
- package/framework/js/components/ui-components/value-display.js +133 -0
- package/framework/js/components/ui-components/video-player.js +686 -0
- package/framework/js/core/component-catalog.js +121 -0
- package/framework/js/core/event-bus.js +178 -0
- package/framework/js/core/interaction-catalog.js +149 -0
- package/framework/js/dev/runtime-linter.js +1725 -0
- package/framework/js/drivers/cmi5-driver.js +768 -0
- package/framework/js/drivers/driver-factory.js +77 -0
- package/framework/js/drivers/driver-interface.js +110 -0
- package/framework/js/drivers/http-driver-base.js +241 -0
- package/framework/js/drivers/lti-driver.js +508 -0
- package/framework/js/drivers/proxy-driver.js +444 -0
- package/framework/js/drivers/scorm-12-driver.js +560 -0
- package/framework/js/drivers/scorm-2004-driver.js +775 -0
- package/framework/js/drivers/scorm-driver-base.js +112 -0
- package/framework/js/engagement/engagement-manager.js +404 -0
- package/framework/js/engagement/engagement-progress.js +191 -0
- package/framework/js/engagement/engagement-trackers.js +215 -0
- package/framework/js/engagement/requirement-strategies.js +268 -0
- package/framework/js/main.js +727 -0
- package/framework/js/managers/accessibility-manager.js +499 -0
- package/framework/js/managers/assessment-manager.js +230 -0
- package/framework/js/managers/audio-manager.js +944 -0
- package/framework/js/managers/comment-manager.js +88 -0
- package/framework/js/managers/flag-manager.js +86 -0
- package/framework/js/managers/interaction-manager.js +254 -0
- package/framework/js/managers/interaction-registry.js +96 -0
- package/framework/js/managers/objective-manager.js +423 -0
- package/framework/js/managers/score-manager.js +441 -0
- package/framework/js/managers/video-manager.js +536 -0
- package/framework/js/navigation/Breadcrumbs.js +234 -0
- package/framework/js/navigation/NavigationActions.js +1132 -0
- package/framework/js/navigation/NavigationState.js +276 -0
- package/framework/js/navigation/NavigationUI.js +574 -0
- package/framework/js/navigation/document-gallery.js +357 -0
- package/framework/js/navigation/navigation-helpers.js +175 -0
- package/framework/js/navigation/navigation-validators.js +174 -0
- package/framework/js/state/index.js +8 -0
- package/framework/js/state/lms-connection.js +482 -0
- package/framework/js/state/lms-error-utils.js +58 -0
- package/framework/js/state/state-commits.js +200 -0
- package/framework/js/state/state-domains.js +86 -0
- package/framework/js/state/state-manager.js +502 -0
- package/framework/js/state/state-validation.js +311 -0
- package/framework/js/state/transaction-log.js +41 -0
- package/framework/js/state/xapi-statement-service.js +325 -0
- package/framework/js/utilities/access-control.js +99 -0
- package/framework/js/utilities/breakpoint-manager.js +315 -0
- package/framework/js/utilities/canvas-slide.js +35 -0
- package/framework/js/utilities/conditional-display.js +388 -0
- package/framework/js/utilities/course-channel.js +214 -0
- package/framework/js/utilities/course-helpers.js +420 -0
- package/framework/js/utilities/data-reporter.js +273 -0
- package/framework/js/utilities/error-reporter.js +313 -0
- package/framework/js/utilities/hotspot-helper.js +341 -0
- package/framework/js/utilities/icons.js +348 -0
- package/framework/js/utilities/logger.js +92 -0
- package/framework/js/utilities/markdown-renderer.js +45 -0
- package/framework/js/utilities/scroll-tracker.js +68 -0
- package/framework/js/utilities/ui-initializer.js +146 -0
- package/framework/js/utilities/utilities.js +293 -0
- package/framework/js/utilities/view-manager.js +227 -0
- package/framework/js/validation/html-validators.js +422 -0
- package/framework/js/validation/scorm-validators.js +438 -0
- package/framework/js/vendor/pipwerks.js +931 -0
- package/framework/scripts/generate-narration.js +629 -0
- package/framework/scripts/tts-providers/azure-provider.js +178 -0
- package/framework/scripts/tts-providers/base-provider.js +81 -0
- package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
- package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
- package/framework/scripts/tts-providers/google-provider.js +272 -0
- package/framework/scripts/tts-providers/index.js +158 -0
- package/framework/scripts/tts-providers/openai-provider.js +143 -0
- package/framework/version.json +63 -0
- package/lib/authoring-api.js +919 -0
- package/lib/build-linter.js +450 -0
- package/lib/build-packaging.js +186 -0
- package/lib/build.js +88 -0
- package/lib/cloud.js +691 -0
- package/lib/convert.js +341 -0
- package/lib/course-parser.js +936 -0
- package/lib/course-writer.js +258 -0
- package/lib/create.js +248 -0
- package/lib/css-index.js +237 -0
- package/lib/dev.js +51 -0
- package/lib/export-content.js +1246 -0
- package/lib/headless-browser.js +413 -0
- package/lib/import.js +377 -0
- package/lib/index.js +80 -0
- package/lib/info.js +79 -0
- package/lib/interaction-formatters.js +568 -0
- package/lib/manifest/cmi5-manifest.js +63 -0
- package/lib/manifest/lti-tool-config.js +53 -0
- package/lib/manifest/manifest-factory.js +99 -0
- package/lib/manifest/scorm-12-manifest.js +61 -0
- package/lib/manifest/scorm-2004-manifest.js +94 -0
- package/lib/manifest/scorm-proxy-manifest.js +104 -0
- package/lib/manifest-parser.js +96 -0
- package/lib/mcp-prompts.js +753 -0
- package/lib/mcp-server.js +316 -0
- package/lib/narration.js +53 -0
- package/lib/pdf-structure.js +142 -0
- package/lib/preview-export.js +231 -0
- package/lib/preview-routes-api.js +662 -0
- package/lib/preview-routes-editing.js +159 -0
- package/lib/preview-routes-lms.js +230 -0
- package/lib/preview-server.js +564 -0
- package/lib/project-utils.js +269 -0
- package/lib/proxy-templates/proxy.html +68 -0
- package/lib/proxy-templates/scorm-bridge.js +112 -0
- package/lib/scaffold.js +193 -0
- package/lib/schema-extractor.js +361 -0
- package/lib/slide-source-editor.js +586 -0
- package/lib/stub-player/app-viewer.js +195 -0
- package/lib/stub-player/app.js +370 -0
- package/lib/stub-player/catalog-panel.js +312 -0
- package/lib/stub-player/config-panel.js +1303 -0
- package/lib/stub-player/content-generator.js +586 -0
- package/lib/stub-player/content-viewer.js +173 -0
- package/lib/stub-player/debug-panel.js +420 -0
- package/lib/stub-player/edit-mode.js +922 -0
- package/lib/stub-player/edit-utils.js +400 -0
- package/lib/stub-player/header-bar.js +354 -0
- package/lib/stub-player/interaction-editor.js +210 -0
- package/lib/stub-player/interactions-panel.js +565 -0
- package/lib/stub-player/lms-api.js +1094 -0
- package/lib/stub-player/login-screen.js +74 -0
- package/lib/stub-player/outline-mode.js +689 -0
- package/lib/stub-player/styles/_assessments-panel.css +245 -0
- package/lib/stub-player/styles/_base.css +89 -0
- package/lib/stub-player/styles/_catalog-icons.css +96 -0
- package/lib/stub-player/styles/_catalog-panel.css +291 -0
- package/lib/stub-player/styles/_config-panel.css +636 -0
- package/lib/stub-player/styles/_content-viewer.css +834 -0
- package/lib/stub-player/styles/_debug-panel.css +576 -0
- package/lib/stub-player/styles/_edit-mode.css +128 -0
- package/lib/stub-player/styles/_header-bar.css +343 -0
- package/lib/stub-player/styles/_interaction-editor.css +140 -0
- package/lib/stub-player/styles/_interactions-panel.css +1038 -0
- package/lib/stub-player/styles/_login-screen.css +102 -0
- package/lib/stub-player/styles/_outline-mode.css +752 -0
- package/lib/stub-player/styles.css +15 -0
- package/lib/stub-player.js +160 -0
- package/lib/test-data-reporting.js +176 -0
- package/lib/test-error-reporting.js +146 -0
- package/lib/token.js +86 -0
- package/lib/upgrade.js +257 -0
- package/lib/validation-rules.js +517 -0
- package/lib/vite-plugin-content-discovery.js +296 -0
- package/package.json +108 -0
- package/schemas/XMLSchema.dtd +402 -0
- package/schemas/adlcp_v1p3.xsd +111 -0
- package/schemas/adlnav_v1p3.xsd +61 -0
- package/schemas/adlseq_v1p3.xsd +93 -0
- package/schemas/common/anyElement.xsd +27 -0
- package/schemas/common/dataTypes.xsd +138 -0
- package/schemas/common/elementNames.xsd +767 -0
- package/schemas/common/elementTypes.xsd +786 -0
- package/schemas/common/rootElement.xsd +31 -0
- package/schemas/common/vocabTypes.xsd +345 -0
- package/schemas/common/vocabValues.xsd +257 -0
- package/schemas/datatypes.dtd +203 -0
- package/schemas/ims_xml.xsd +35 -0
- package/schemas/imscp_v1p1.xsd +368 -0
- package/schemas/imsss_v1p0.xsd +67 -0
- package/schemas/imsss_v1p0auxresource.xsd +19 -0
- package/schemas/imsss_v1p0control.xsd +20 -0
- package/schemas/imsss_v1p0delivery.xsd +17 -0
- package/schemas/imsss_v1p0limit.xsd +47 -0
- package/schemas/imsss_v1p0objective.xsd +67 -0
- package/schemas/imsss_v1p0random.xsd +16 -0
- package/schemas/imsss_v1p0rollup.xsd +46 -0
- package/schemas/imsss_v1p0seqrule.xsd +108 -0
- package/schemas/imsss_v1p0util.xsd +94 -0
- package/schemas/license.txt +17 -0
- package/schemas/lom.xsd +102 -0
- package/schemas/lomCustom.xsd +62 -0
- package/schemas/lomLoose.xsd +62 -0
- package/schemas/lomStrict.xsd +62 -0
- package/schemas/xml.xsd +81 -0
- package/template/.env.example +92 -0
- package/template/course/assets/audio/example-intro.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
- package/template/course/assets/docs/example_md_1.md +39 -0
- package/template/course/assets/docs/example_md_2.md +41 -0
- package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
- package/template/course/assets/docs/example_pdf_2.pdf +0 -0
- package/template/course/assets/images/course-architecture.svg +36 -0
- package/template/course/assets/images/logo.svg +14 -0
- package/template/course/assets/widgets/counter-demo.html +190 -0
- package/template/course/assets/widgets/gravity-painter.html +384 -0
- package/template/course/course-config.js +539 -0
- package/template/course/icons.js +19 -0
- package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
- package/template/course/slides/example-course-structure.js +138 -0
- package/template/course/slides/example-final-exam.js +144 -0
- package/template/course/slides/example-finishing.js +127 -0
- package/template/course/slides/example-interactions-showcase.js +615 -0
- package/template/course/slides/example-preview-tour.js +129 -0
- package/template/course/slides/example-remedial.js +143 -0
- package/template/course/slides/example-summary.js +103 -0
- package/template/course/slides/example-ui-showcase.js +1805 -0
- package/template/course/slides/example-welcome.js +123 -0
- package/template/course/slides/example-workflow.js +140 -0
- package/template/course/theme.css +165 -0
- package/template/eslint.config.js +47 -0
- package/template/package.json +28 -0
- package/template/vite.config.js +339 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# [Course Title]
|
|
2
|
+
|
|
3
|
+
> **Intended Audience: AI Agents** — This is a machine-readable template for AI agents. For human-readable documentation, see `USER_GUIDE.md`.
|
|
4
|
+
|
|
5
|
+
* **Course ID:** `[COURSE-ID]`
|
|
6
|
+
* **Target Audience:** `[Audience]`
|
|
7
|
+
* **Estimated Duration:** `[X] minutes`
|
|
8
|
+
* **Layout Type:** `traditional` | `article` | `focused` | `presentation` | `canvas`
|
|
9
|
+
* **Architecture:** `Single-SCO`
|
|
10
|
+
* **Key Learning Objectives:** `[Human-readable objective 1]`, `[Objective 2]`, `[Objective 3]`
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Course Overview & Strategy
|
|
15
|
+
|
|
16
|
+
*This section defines the high-level architecture, scoring, and success criteria.*
|
|
17
|
+
|
|
18
|
+
### A. Completion & Success Rules
|
|
19
|
+
|
|
20
|
+
* **Completion:** [e.g., "Must view all slides and pass Final Exam"]
|
|
21
|
+
* **Success:** [e.g., "Final Exam score >= 80%"]
|
|
22
|
+
* **Scoring Strategy:** [e.g., "Weighted: Final Exam (100%)" or "Average of all quizzes"]
|
|
23
|
+
|
|
24
|
+
### B. Objectives & Tracking Logic
|
|
25
|
+
|
|
26
|
+
*Defines the SCORM objectives used for tracking. Maps to `course-config.js`.*
|
|
27
|
+
|
|
28
|
+
| ID | Description | Criteria Type | Details |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| `obj-intro` | Completed introduction | `slideVisited` | `slide-01` |
|
|
31
|
+
| `obj-core` | Reviewed core content | `allSlidesVisited` | `[slide-02, slide-03]` |
|
|
32
|
+
| `obj-exam` | Passed Final Exam | `Manual` | Set by `assess-final` |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. Content Structure (High-Level)
|
|
37
|
+
|
|
38
|
+
*Master table of contents showing flow, gating, and logic.*
|
|
39
|
+
|
|
40
|
+
* **Module 1: Introduction**
|
|
41
|
+
* `slide-01`: [Welcome]
|
|
42
|
+
* **Module 2: Core Concepts** (`Gate:` Requires `obj-intro`)
|
|
43
|
+
* `slide-02`: [Concept A]
|
|
44
|
+
* `slide-03`: [Concept B]
|
|
45
|
+
* **Module 3: Assessment**
|
|
46
|
+
* `assess-final`: [Final Exam]
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 3. Slide-by-Slide Breakdown
|
|
51
|
+
|
|
52
|
+
*Detailed specification for every slide. This is the blueprint for development.*
|
|
53
|
+
|
|
54
|
+
### Module 1: Introduction
|
|
55
|
+
|
|
56
|
+
#### `slide-01`: [Title]
|
|
57
|
+
|
|
58
|
+
**Visual Concept:**
|
|
59
|
+
|
|
60
|
+
[Describe the layout, imagery, and visual hierarchy. E.g., "Split screen: Left side has a welcome video placeholder, Right side has the course title and start button."]
|
|
61
|
+
|
|
62
|
+
**Content:**
|
|
63
|
+
|
|
64
|
+
* **Heading:** [H1 Title]
|
|
65
|
+
* **Body:**
|
|
66
|
+
* [Paragraph 1 text...]
|
|
67
|
+
* [Bullet points...]
|
|
68
|
+
* **UI Elements:**
|
|
69
|
+
* Button: "Start Course"
|
|
70
|
+
|
|
71
|
+
**Narration:** (if audio is used)
|
|
72
|
+
|
|
73
|
+
> [Narration script that complements—not duplicates—on-screen content. Expand on key points, provide context, or guide attention rather than reading text verbatim.]
|
|
74
|
+
|
|
75
|
+
**Navigation & Tracking:**
|
|
76
|
+
|
|
77
|
+
* **Nav:** Next button unlocks after media completes.
|
|
78
|
+
* **Track:** `slideVisited` (completes `obj-intro`).
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Module 2: Core Concepts
|
|
83
|
+
|
|
84
|
+
#### `slide-02`: [Title]
|
|
85
|
+
|
|
86
|
+
**Visual Concept:**
|
|
87
|
+
|
|
88
|
+
[E.g., "Interactive Accordion. Three collapsible sections labeled 'Plan', 'Do', 'Check'."]
|
|
89
|
+
|
|
90
|
+
**Content:**
|
|
91
|
+
|
|
92
|
+
* **Intro Text:** Click each step to learn more.
|
|
93
|
+
* **Accordion Item 1 (Plan):** [Content for item 1]
|
|
94
|
+
* **Accordion Item 2 (Do):** [Content for item 2]
|
|
95
|
+
* **Accordion Item 3 (Check):** [Content for item 3]
|
|
96
|
+
|
|
97
|
+
**Interactions:**
|
|
98
|
+
|
|
99
|
+
* **Type:** Accordion
|
|
100
|
+
* **Behavior:** User must click all 3 items to proceed.
|
|
101
|
+
|
|
102
|
+
**Navigation & Tracking:**
|
|
103
|
+
|
|
104
|
+
* **Engagement:** `viewAllTabs` required to unlock Next button.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
#### `slide-03`: [Practice Scenario]
|
|
109
|
+
|
|
110
|
+
**Visual Concept:**
|
|
111
|
+
|
|
112
|
+
[E.g., "Scenario Layout. Context text on top, multiple choice question below."]
|
|
113
|
+
|
|
114
|
+
**Content:**
|
|
115
|
+
|
|
116
|
+
* **Scenario:** [Context paragraph...]
|
|
117
|
+
* **Question:** [Question text]
|
|
118
|
+
* **Options:**
|
|
119
|
+
* A) [Text]
|
|
120
|
+
* B) [Text] (Correct)
|
|
121
|
+
* C) [Text]
|
|
122
|
+
* **Feedback:**
|
|
123
|
+
* **Correct:** [Text]
|
|
124
|
+
* **Incorrect:** [Text]
|
|
125
|
+
|
|
126
|
+
**Interactions:**
|
|
127
|
+
|
|
128
|
+
* **Type:** Multiple Choice (Practice)
|
|
129
|
+
* **ID:** `practice-01`
|
|
130
|
+
|
|
131
|
+
**Navigation & Tracking:**
|
|
132
|
+
|
|
133
|
+
* **Track:** Contributes to `obj-core`.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### Module 3: Assessment
|
|
138
|
+
|
|
139
|
+
#### `assess-final`: [Final Exam]
|
|
140
|
+
|
|
141
|
+
**Strategy:**
|
|
142
|
+
|
|
143
|
+
* **Pool:** Select 10 questions from a bank of 15.
|
|
144
|
+
* **Pass Score:** 80%
|
|
145
|
+
* **Retries:** 2 attempts allowed.
|
|
146
|
+
|
|
147
|
+
**Question Bank:**
|
|
148
|
+
|
|
149
|
+
**Q1** (`q1-id`): [Question Text]
|
|
150
|
+
|
|
151
|
+
* **Type:** Multiple Choice
|
|
152
|
+
* **Options:** A) [Text], B) [Text] (Correct)
|
|
153
|
+
* **Feedback:** [Explain why]
|
|
154
|
+
|
|
155
|
+
**Q2** (`q2-id`): [Question Text]
|
|
156
|
+
|
|
157
|
+
* **Type:** True/False
|
|
158
|
+
* **Correct:** True
|
|
159
|
+
* **Feedback:** [Explain why]
|
|
160
|
+
|
|
161
|
+
... *(continue for all questions)* ...
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# CourseCode Data Model Reference
|
|
2
|
+
|
|
3
|
+
> **Intended Audience: AI Agents** — This document is a machine-readable data model reference for AI agents. For human-readable documentation, see `USER_GUIDE.md`.
|
|
4
|
+
|
|
5
|
+
Complete reference for all learner data managed by the framework. The framework uses a **domain-based state model** that is transport-agnostic — the same domains exist regardless of whether the delivery format is SCORM 1.2, SCORM 2004, cmi5, or LTI. The state module ([state/](../js/state/)) routes each domain to the appropriate storage mechanism.
|
|
6
|
+
|
|
7
|
+
> [!TIP]
|
|
8
|
+
> **For framework internals** (drivers, managers, architecture), see [`FRAMEWORK_GUIDE.md`](./FRAMEWORK_GUIDE.md).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Storage Architecture
|
|
13
|
+
|
|
14
|
+
```mermaid
|
|
15
|
+
graph TD
|
|
16
|
+
subgraph "Framework Domains (suspend_data)"
|
|
17
|
+
META["_meta"]
|
|
18
|
+
NAV["navigation"]
|
|
19
|
+
ENG["engagement"]
|
|
20
|
+
IR["interactionResponses"]
|
|
21
|
+
ASSESS["assessment_*"]
|
|
22
|
+
FLAGS["flags"]
|
|
23
|
+
SESSION["sessionData"]
|
|
24
|
+
A11Y["accessibility"]
|
|
25
|
+
AUDIO["audio"]
|
|
26
|
+
VIDEO["video"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
subgraph "CMI-Backed Domains (SCORM only)"
|
|
30
|
+
OBJ["objectives"]
|
|
31
|
+
INT["interactions"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
subgraph "Direct CMI Writes"
|
|
35
|
+
SCORE["cmi.score.*"]
|
|
36
|
+
COMP["cmi.completion_status"]
|
|
37
|
+
SUCC["cmi.success_status"]
|
|
38
|
+
PROG["cmi.progress_measure"]
|
|
39
|
+
LOC["cmi.location"]
|
|
40
|
+
EXIT["cmi.exit"]
|
|
41
|
+
SESSTIME["cmi.session_time"]
|
|
42
|
+
COMMENTS["cmi.comments_from_learner.*"]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
SD["suspend_data (JSON blob)"]
|
|
46
|
+
|
|
47
|
+
META & NAV & ENG & IR & ASSESS & FLAGS & SESSION & A11Y & AUDIO & VIDEO --> SD
|
|
48
|
+
OBJ -->|"SCORM: cmi.objectives.*"| CMI["CMI Data Model"]
|
|
49
|
+
OBJ -->|"cmi5/LTI: suspend_data"| SD
|
|
50
|
+
INT -->|"SCORM: cmi.interactions.*"| CMI
|
|
51
|
+
INT -->|"cmi5/LTI: suspend_data"| SD
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Per-Format Routing
|
|
55
|
+
|
|
56
|
+
| Storage | SCORM 1.2 / 2004 | cmi5 | LTI |
|
|
57
|
+
|---------|-------------------|------|-----|
|
|
58
|
+
| `suspend_data` domains | ✅ | ✅ | ✅ (host provides) |
|
|
59
|
+
| CMI-backed `objectives` | `cmi.objectives.*` | suspend_data | suspend_data |
|
|
60
|
+
| CMI-backed `interactions` | `cmi.interactions.*` (append-only) | suspend_data | suspend_data |
|
|
61
|
+
| Direct CMI writes | ✅ | Via xAPI statements | Score via LTI AGS |
|
|
62
|
+
| `cmi.location` | ✅ | ✅ (suspend_data key) | ✅ (host provides) |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Access Pattern
|
|
67
|
+
|
|
68
|
+
All state access flows through `stateManager` — the sole public API from the [state/](../js/state/) module:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import stateManager from '../state/index.js';
|
|
72
|
+
|
|
73
|
+
// Read/write domain state
|
|
74
|
+
stateManager.getDomainState('navigation');
|
|
75
|
+
stateManager.setDomainState('navigation', { visitedSlides: ['intro', 'slide-01'] });
|
|
76
|
+
|
|
77
|
+
// Semantic LMS operations (delegated to the driver internally)
|
|
78
|
+
stateManager.reportScore({ raw: 85, scaled: 0.85, min: 0, max: 100 });
|
|
79
|
+
stateManager.reportCompletion('completed');
|
|
80
|
+
stateManager.setBookmark('slide-03');
|
|
81
|
+
stateManager.flush(); // Force immediate commit
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> [!IMPORTANT]
|
|
85
|
+
> **Never import `lms-connection.js` or driver modules directly.** All LMS communication flows through `stateManager`.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Domain Schemas
|
|
90
|
+
|
|
91
|
+
### `_meta`
|
|
92
|
+
|
|
93
|
+
Internal metadata for state versioning. Managed by [state-validation.js](../js/state/state-validation.js).
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
{
|
|
97
|
+
schemaVersion: number, // e.g. 1 — used for state migration on course updates
|
|
98
|
+
createdAt: string, // ISO 8601 timestamp
|
|
99
|
+
lastValidatedAt?: string // Set after validation against course structure
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### `navigation`
|
|
106
|
+
|
|
107
|
+
Slide visit tracking. Managed by [NavigationState.js](../js/navigation/NavigationState.js).
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
{
|
|
111
|
+
visitedSlides: string[] // Array of slide IDs the learner has visited
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
> `currentSlideIndex` is runtime-only (not persisted). `cmi.location` is the authoritative bookmark.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### `engagement`
|
|
120
|
+
|
|
121
|
+
Engagement tracking for navigation gating. Managed by [engagement-manager.js](../js/managers/engagement-manager.js).
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
{
|
|
125
|
+
[slideId: string]: {
|
|
126
|
+
required: boolean,
|
|
127
|
+
complete: boolean,
|
|
128
|
+
tracked: {
|
|
129
|
+
tabsViewed: string[],
|
|
130
|
+
tabsTotal: number,
|
|
131
|
+
accordionPanelsViewed: string[],
|
|
132
|
+
accordionPanelsTotal: number,
|
|
133
|
+
flipCardsViewed: string[],
|
|
134
|
+
flipCardsTotal: number,
|
|
135
|
+
flipCardsRegistered: string[],
|
|
136
|
+
interactiveImageHotspotsViewed: string[],
|
|
137
|
+
interactiveImageHotspotsTotal: number,
|
|
138
|
+
modalsViewed: string[],
|
|
139
|
+
modalsTotal: number,
|
|
140
|
+
modalsAudioComplete: string[],
|
|
141
|
+
lightboxesViewed: string[],
|
|
142
|
+
lightboxesTotal: number,
|
|
143
|
+
timelineEventsViewed: string[],
|
|
144
|
+
timelineEventsTotal: number,
|
|
145
|
+
interactionsCompleted: { [interactionId: string]: { completed: boolean, correct: boolean } },
|
|
146
|
+
scrollDepth: number, // 0-100
|
|
147
|
+
audioComplete: boolean, // Slide-level audio
|
|
148
|
+
standaloneAudioComplete: string[],
|
|
149
|
+
videoComplete: boolean, // Slide-level video
|
|
150
|
+
standaloneVideoComplete: string[]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
> Only slides with `engagement.required = true` in the course config get state entries. Non-required slides are skipped to save space.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### `interactionResponses`
|
|
161
|
+
|
|
162
|
+
Learner responses for standalone interactions (outside assessments). Managed by [interaction-base.js](../js/components/interactions/interaction-base.js).
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
{
|
|
166
|
+
[interactionId: string]: {
|
|
167
|
+
response: any, // Format depends on interaction type
|
|
168
|
+
submitted: boolean // Whether the learner has submitted (clicked Check)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Dual Storage Pattern
|
|
174
|
+
|
|
175
|
+
Standalone interactions use two storage mechanisms:
|
|
176
|
+
|
|
177
|
+
| Storage | Domain | Purpose | Pattern |
|
|
178
|
+
|---------|--------|---------|---------|
|
|
179
|
+
| `cmi.interactions.n.*` | `interactions` | LMS reporting, audit trail | Append-only (each Check = new record) |
|
|
180
|
+
| `suspend_data` | `interactionResponses` | UI state restoration | Key-value (latest state only) |
|
|
181
|
+
|
|
182
|
+
**Why?** CMI interactions are append-only by spec — same ID appears multiple times (one per attempt). Can't use CMI for restoration, so `interactionResponses` stores `{ response, submitted }` for O(1) lookup.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### `assessment_${assessmentId}`
|
|
187
|
+
|
|
188
|
+
Per-assessment state. Each assessment gets its own domain (e.g. `assessment_final-exam`). Managed by [AssessmentState.js](../js/assessment/AssessmentState.js).
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
{
|
|
192
|
+
summary: {
|
|
193
|
+
attempts: number,
|
|
194
|
+
passed: boolean,
|
|
195
|
+
lastResults: {
|
|
196
|
+
scorePercentage: number, // 0-100
|
|
197
|
+
scoreRaw: number,
|
|
198
|
+
scoreMax: number,
|
|
199
|
+
passed: boolean,
|
|
200
|
+
correctCount: number,
|
|
201
|
+
incorrectCount: number,
|
|
202
|
+
totalQuestions: number
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
session: {
|
|
206
|
+
currentView: string, // 'instructions' | 'question' | 'results' | 'review'
|
|
207
|
+
currentQuestionIndex: number,
|
|
208
|
+
startTime: number, // Date.now() timestamp
|
|
209
|
+
submitted: boolean,
|
|
210
|
+
attemptNumber: number,
|
|
211
|
+
responses: { [questionId: string]: any },
|
|
212
|
+
selectedQuestions: string[], // Question IDs (may be randomized subset)
|
|
213
|
+
reviewReached: boolean
|
|
214
|
+
},
|
|
215
|
+
discardedAttempts: any[] // Previous attempt data kept for audit
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `flags`
|
|
222
|
+
|
|
223
|
+
Arbitrary key-value flags for custom course logic. Managed by [flag-manager.js](../js/managers/flag-manager.js).
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
{
|
|
227
|
+
[key: string]: any // Boolean, string, number — any serializable value
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### `sessionData`
|
|
234
|
+
|
|
235
|
+
Slide timing data. Managed by [AppActions.js](../js/app/AppActions.js).
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
{
|
|
239
|
+
slideStartTimes: { [slideId: string]: number }, // Date.now() timestamps
|
|
240
|
+
slideDurations: { [slideId: string]: number } // Accumulated milliseconds
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### `accessibility`
|
|
247
|
+
|
|
248
|
+
Learner accessibility preferences. Managed by [accessibility-manager.js](../js/managers/accessibility-manager.js).
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
{
|
|
252
|
+
theme: 'light' | 'dark',
|
|
253
|
+
fontSize: 'normal' | 'large',
|
|
254
|
+
highContrast: boolean,
|
|
255
|
+
reducedMotion: boolean
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### `audio`
|
|
262
|
+
|
|
263
|
+
Audio playback state. Managed by [audio-manager.js](../js/managers/audio-manager.js).
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
{
|
|
267
|
+
positions: { [contextId: string]: number }, // Playback position in seconds
|
|
268
|
+
completions: { [contextId: string]: boolean }, // Whether audio has been completed
|
|
269
|
+
muted: boolean // Learner's mute preference
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### `video`
|
|
276
|
+
|
|
277
|
+
Video playback state. Managed by [video-manager.js](../js/managers/video-manager.js).
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
{
|
|
281
|
+
positions: { [contextId: string]: number }, // Playback position in seconds
|
|
282
|
+
completions: { [contextId: string]: boolean }, // Whether video has been completed
|
|
283
|
+
isMuted: boolean // Learner's mute preference
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### `objectives` (CMI-backed)
|
|
290
|
+
|
|
291
|
+
Learning objectives. Managed by [objective-manager.js](../js/managers/objective-manager.js). Routed to `cmi.objectives.*` on SCORM, or `suspend_data` on cmi5/LTI.
|
|
292
|
+
|
|
293
|
+
```js
|
|
294
|
+
{
|
|
295
|
+
[objectiveId: string]: {
|
|
296
|
+
id: string,
|
|
297
|
+
completion_status: 'completed' | 'incomplete',
|
|
298
|
+
success_status: 'passed' | 'failed' | 'unknown',
|
|
299
|
+
score: number | null // 0-100
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### `interactions` (CMI-backed)
|
|
307
|
+
|
|
308
|
+
Interaction records for LMS reporting. Managed by [interaction-manager.js](../js/managers/interaction-manager.js). **Append-only** on SCORM (`cmi.interactions.*`), or in `suspend_data` on cmi5/LTI.
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
{
|
|
312
|
+
id: string,
|
|
313
|
+
type: string, // 'choice' | 'true-false' | 'fill-in' | 'matching' | 'sequencing' | 'numeric' | 'other'
|
|
314
|
+
learner_response: string, // SCORM wire format ([,] delimiters, [.] pair separators)
|
|
315
|
+
result: string, // 'correct' | 'incorrect' | 'neutral'
|
|
316
|
+
timestamp: string, // ISO 8601
|
|
317
|
+
correct_responses?: string,
|
|
318
|
+
latency?: string,
|
|
319
|
+
description?: string,
|
|
320
|
+
weighting?: number
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Direct CMI Writes
|
|
327
|
+
|
|
328
|
+
These are written directly by `stateManager` semantic methods and are **not** part of domain state. They flow through the driver to the LMS.
|
|
329
|
+
|
|
330
|
+
| CMI Key | Written By | Value | Notes |
|
|
331
|
+
|---------|-----------|-------|-------|
|
|
332
|
+
| `cmi.score.raw` | `stateManager.reportScore()` | `0` – `100` | Via [score-manager.js](../js/managers/score-manager.js) |
|
|
333
|
+
| `cmi.score.scaled` | `stateManager.reportScore()` | `0` – `1` | raw / 100 |
|
|
334
|
+
| `cmi.score.min` | `stateManager.reportScore()` | `0` | Always 0 |
|
|
335
|
+
| `cmi.score.max` | `stateManager.reportScore()` | `100` | Always 100 |
|
|
336
|
+
| `cmi.completion_status` | `stateManager.reportCompletion()` | `completed` / `incomplete` | |
|
|
337
|
+
| `cmi.success_status` | `stateManager.reportSuccess()` | `passed` / `failed` / `unknown` | |
|
|
338
|
+
| `cmi.progress_measure` | `stateManager.updateProgressMeasure()` | `0` – `1` | Fraction of slides visited |
|
|
339
|
+
| `cmi.location` | `stateManager.setBookmark()` | Slide ID string | Authoritative bookmark |
|
|
340
|
+
| `cmi.exit` | `stateManager.exitCourseWithSuspend()` | `suspend` / `normal` | Set on exit |
|
|
341
|
+
| `cmi.session_time` | `stateManager.terminate()` | ISO 8601 duration | Calculated on terminate |
|
|
342
|
+
| `cmi.comments_from_learner.*` | [comment-manager.js](../js/managers/comment-manager.js) | Comment text + timestamp | Append-only |
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## suspend_data Serialization
|
|
347
|
+
|
|
348
|
+
All domain states combine into a single JSON object stored in `cmi.suspend_data`:
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
{
|
|
352
|
+
_meta: { schemaVersion: 1, createdAt: '2026-...' },
|
|
353
|
+
navigation: { visitedSlides: [...] },
|
|
354
|
+
engagement: { ... },
|
|
355
|
+
interactionResponses: { ... },
|
|
356
|
+
assessment_quiz1: { ... },
|
|
357
|
+
flags: { ... },
|
|
358
|
+
sessionData: { ... },
|
|
359
|
+
accessibility: { ... },
|
|
360
|
+
audio: { ... },
|
|
361
|
+
video: { ... },
|
|
362
|
+
// cmi5/LTI only (SCORM uses CMI arrays):
|
|
363
|
+
objectives: { ... },
|
|
364
|
+
interactions: { ... }
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
> [!NOTE]
|
|
369
|
+
> **Compression:** `cmi.suspend_data` is automatically compressed via `lz-string` (UTF16) in the driver layer. This is transparent to all consumers.
|
|
370
|
+
|
|
371
|
+
> [!NOTE]
|
|
372
|
+
> For SCORM, `objectives` and `interactions` are stored in CMI arrays and **not** included in `suspend_data`. For cmi5 and LTI, they are included since those formats don't support CMI data model arrays.
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Write Batching
|
|
377
|
+
|
|
378
|
+
LMS writes are auto-batched with a 500ms debounce. Rapid `setDomainState` calls within this window combine into a single commit.
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
// These writes auto-batch into 1 commit
|
|
382
|
+
stateManager.setDomainState('navigation', navState);
|
|
383
|
+
stateManager.setDomainState('engagement', engState);
|
|
384
|
+
// → Single commit fires after 500ms idle
|
|
385
|
+
|
|
386
|
+
// Force immediate commit for critical paths
|
|
387
|
+
stateManager.flush();
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Critical actions** (exit, terminate) automatically flush pending writes before proceeding.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## State Validation & Migration
|
|
395
|
+
|
|
396
|
+
Handles LMS data mismatches when course structure changes after learners have started:
|
|
397
|
+
|
|
398
|
+
| Behavior | Dev Mode | Prod Mode |
|
|
399
|
+
|----------|----------|----------|
|
|
400
|
+
| Orphaned slide in navigation | Warns + filters | Silently removes |
|
|
401
|
+
| Orphaned engagement data | Warns | Silently removes |
|
|
402
|
+
| Schema version newer (downgrade) | Throws error | Resets to fresh state |
|
|
403
|
+
| Schema version older (upgrade) | Runs migrations | Runs migrations |
|
|
404
|
+
|
|
405
|
+
**Setup:** `stateManager.setCourseValidationConfig(config)` called in `main.js` before `initialize()`.
|
|
406
|
+
|
|
407
|
+
**Schema versioning:** `STATE_SCHEMA_VERSION` in [state-validation.js](../js/state/state-validation.js). Add migration functions in `STATE_MIGRATIONS` when incrementing.
|
|
408
|
+
|
|
409
|
+
**Event:** `state:recovered` emitted when prod mode gracefully recovers.
|