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,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file api-interactions.js
|
|
3
|
+
* Interaction discovery, state access, mutation, and evaluation methods
|
|
4
|
+
* for the CourseCodeAutomation API.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import interactionRegistry from '../managers/interaction-registry.js';
|
|
8
|
+
import { courseConfig } from '../../../course/course-config.js';
|
|
9
|
+
import { recordInteractionResult } from '../components/interactions/interaction-base.js';
|
|
10
|
+
import { logger } from '../utilities/logger.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates interaction API methods bound to the shared logTrace function.
|
|
14
|
+
* @param {Function} logTrace - Shared trace logger
|
|
15
|
+
* @returns {Object} Interaction API methods
|
|
16
|
+
*/
|
|
17
|
+
export function createInteractionMethods(logTrace) {
|
|
18
|
+
return {
|
|
19
|
+
listInteractions() {
|
|
20
|
+
const interactions = interactionRegistry.getAll();
|
|
21
|
+
const simplifiedList = interactions.map(i => ({
|
|
22
|
+
id: i.id,
|
|
23
|
+
type: i.type,
|
|
24
|
+
description: i.description
|
|
25
|
+
}));
|
|
26
|
+
logTrace('listInteractions', { count: simplifiedList.length });
|
|
27
|
+
return simplifiedList;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
getInteractionMetadata(interactionId) {
|
|
31
|
+
const entry = interactionRegistry.getAll().find(i => i.id === interactionId);
|
|
32
|
+
if (!entry) {
|
|
33
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" not found on the current slide`);
|
|
34
|
+
}
|
|
35
|
+
logTrace('getInteractionMetadata', { interactionId });
|
|
36
|
+
return {
|
|
37
|
+
id: entry.id,
|
|
38
|
+
type: entry.type,
|
|
39
|
+
description: entry.description
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
getResponse(interactionId) {
|
|
44
|
+
const entry = interactionRegistry.getAll().find(i => i.id === interactionId);
|
|
45
|
+
if (!entry || !entry.instance) {
|
|
46
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" not found or has no instance`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof entry.instance.getResponse !== 'function') {
|
|
50
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" does not support getResponse`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const response = entry.instance.getResponse();
|
|
54
|
+
logTrace('getResponse', { interactionId, response });
|
|
55
|
+
return response;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
getCorrectResponse(interactionId) {
|
|
59
|
+
if (!courseConfig.environment?.automation?.exposeCorrectAnswers) {
|
|
60
|
+
throw new Error('CourseCodeAutomation: getCorrectResponse requires automation.exposeCorrectAnswers=true in course config');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const entry = interactionRegistry.getAll().find(i => i.id === interactionId);
|
|
64
|
+
if (!entry || !entry.instance) {
|
|
65
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" not found or has no instance`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof entry.instance.getCorrectAnswer !== 'function') {
|
|
69
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" does not support getCorrectAnswer`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const correctAnswer = entry.instance.getCorrectAnswer();
|
|
73
|
+
logTrace('getCorrectResponse', { interactionId });
|
|
74
|
+
return correctAnswer;
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
setResponse(interactionId, response) {
|
|
78
|
+
const entry = interactionRegistry.getAll().find(i => i.id === interactionId);
|
|
79
|
+
if (!entry || !entry.instance) {
|
|
80
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" not found or has no instance`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof entry.instance.setResponse !== 'function') {
|
|
84
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" does not support setResponse`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
entry.instance.setResponse(response);
|
|
89
|
+
logTrace('setResponse', { interactionId, response });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logTrace('setResponse:error', { interactionId, response, error: error.message });
|
|
92
|
+
throw new Error(`CourseCodeAutomation: Failed to set response for "${interactionId}": ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
checkAnswer(interactionId) {
|
|
97
|
+
const entry = interactionRegistry.getAll().find(i => i.id === interactionId);
|
|
98
|
+
if (!entry || !entry.instance) {
|
|
99
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" not found or has no instance`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (typeof entry.instance.checkAnswer !== 'function') {
|
|
103
|
+
throw new Error(`CourseCodeAutomation: Interaction "${interactionId}" does not support checkAnswer`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const evaluation = entry.instance.checkAnswer();
|
|
108
|
+
logTrace('checkAnswer', { interactionId, evaluation });
|
|
109
|
+
|
|
110
|
+
const config = entry.config;
|
|
111
|
+
if (!config.controlled) {
|
|
112
|
+
try {
|
|
113
|
+
recordInteractionResult(config, evaluation);
|
|
114
|
+
} catch (recordError) {
|
|
115
|
+
logger.warn(`[CourseCodeAutomation] Failed to record interaction "${interactionId}" to SCORM: ${recordError.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return evaluation;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
logTrace('checkAnswer:error', { interactionId, error: error.message });
|
|
122
|
+
throw new Error(`CourseCodeAutomation: Failed to check answer for "${interactionId}": ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
checkSlideAnswers(getCurrentSlide) {
|
|
127
|
+
const slideId = getCurrentSlide();
|
|
128
|
+
const slideInteractions = interactionRegistry.getAll();
|
|
129
|
+
|
|
130
|
+
if (slideInteractions.length === 0) {
|
|
131
|
+
logTrace('checkSlideAnswers', {
|
|
132
|
+
slideId,
|
|
133
|
+
found: 0,
|
|
134
|
+
message: 'No interactions found on slide'
|
|
135
|
+
});
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const results = [];
|
|
140
|
+
for (const interactionInfo of slideInteractions) {
|
|
141
|
+
try {
|
|
142
|
+
const evaluation = this.checkAnswer(interactionInfo.id);
|
|
143
|
+
results.push({
|
|
144
|
+
interactionId: interactionInfo.id,
|
|
145
|
+
type: interactionInfo.type,
|
|
146
|
+
evaluation
|
|
147
|
+
});
|
|
148
|
+
} catch (error) {
|
|
149
|
+
results.push({
|
|
150
|
+
interactionId: interactionInfo.id,
|
|
151
|
+
type: interactionInfo.type,
|
|
152
|
+
error: error.message
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
logTrace('checkSlideAnswers', {
|
|
158
|
+
slideId,
|
|
159
|
+
total: slideInteractions.length,
|
|
160
|
+
successful: results.filter(r => !r.error).length,
|
|
161
|
+
failed: results.filter(r => r.error).length
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return results;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file api.js
|
|
3
|
+
* @description Public automation API exposed on window.CourseCodeAutomation
|
|
4
|
+
* Provides programmatic control over interactions, navigation, and course state
|
|
5
|
+
* for testing and AI-driven automation.
|
|
6
|
+
*
|
|
7
|
+
* This module is ONLY loaded in development/testing mode and is completely excluded
|
|
8
|
+
* from production builds via Vite's tree-shaking.
|
|
9
|
+
*
|
|
10
|
+
* Delegates to domain-specific modules:
|
|
11
|
+
* api-interactions.js – discovery, state, mutation, evaluation
|
|
12
|
+
* api-engagement.js – engagement, flags, audio
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { logger } from '../utilities/logger.js';
|
|
16
|
+
import { eventBus } from '../core/event-bus.js';
|
|
17
|
+
import * as NavigationActions from '../navigation/NavigationActions.js';
|
|
18
|
+
import { courseConfig } from '../../../course/course-config.js';
|
|
19
|
+
import stateManager from '../state/index.js';
|
|
20
|
+
import accessibilityManager from '../managers/accessibility-manager.js';
|
|
21
|
+
import objectiveManager from '../managers/objective-manager.js';
|
|
22
|
+
|
|
23
|
+
import { createInteractionMethods } from './api-interactions.js';
|
|
24
|
+
import { createEngagementMethods } from './api-engagement.js';
|
|
25
|
+
|
|
26
|
+
/** Automation trace log for observability */
|
|
27
|
+
const automationTrace = [];
|
|
28
|
+
|
|
29
|
+
/** Framework log buffer — captures structured log:warn and log:error events */
|
|
30
|
+
const frameworkLogs = [];
|
|
31
|
+
const MAX_FRAMEWORK_LOGS = 100;
|
|
32
|
+
|
|
33
|
+
eventBus.on('log:warn', (payload) => {
|
|
34
|
+
frameworkLogs.push({ ...payload, timestamp: new Date().toISOString() });
|
|
35
|
+
if (frameworkLogs.length > MAX_FRAMEWORK_LOGS) frameworkLogs.shift();
|
|
36
|
+
});
|
|
37
|
+
eventBus.on('log:error', (payload) => {
|
|
38
|
+
frameworkLogs.push({ ...payload, timestamp: new Date().toISOString() });
|
|
39
|
+
if (frameworkLogs.length > MAX_FRAMEWORK_LOGS) frameworkLogs.shift();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/** Adds an entry to the automation trace log */
|
|
43
|
+
function logTrace(action, details) {
|
|
44
|
+
const entry = {
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
action,
|
|
47
|
+
...details
|
|
48
|
+
};
|
|
49
|
+
automationTrace.push(entry);
|
|
50
|
+
eventBus.emit('automation:trace', entry);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Build delegated method sets
|
|
54
|
+
const interactionMethods = createInteractionMethods(logTrace);
|
|
55
|
+
const engagementMethods = createEngagementMethods(logTrace);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* CourseCodeAutomation API
|
|
59
|
+
* All methods throw errors on failure (no silent failures)
|
|
60
|
+
*/
|
|
61
|
+
const CourseCodeAutomationAPI = {
|
|
62
|
+
// ===== Discovery & Interaction Methods =====
|
|
63
|
+
listInteractions: interactionMethods.listInteractions,
|
|
64
|
+
getInteractionMetadata: interactionMethods.getInteractionMetadata,
|
|
65
|
+
getResponse: interactionMethods.getResponse,
|
|
66
|
+
getCorrectResponse: interactionMethods.getCorrectResponse,
|
|
67
|
+
setResponse: interactionMethods.setResponse,
|
|
68
|
+
checkAnswer: interactionMethods.checkAnswer,
|
|
69
|
+
checkSlideAnswers() {
|
|
70
|
+
return interactionMethods.checkSlideAnswers.call(
|
|
71
|
+
interactionMethods,
|
|
72
|
+
() => this.getCurrentSlide()
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// ===== Navigation Methods =====
|
|
77
|
+
|
|
78
|
+
getToc() {
|
|
79
|
+
const toc = [];
|
|
80
|
+
const flatten = (items) => {
|
|
81
|
+
for (const item of items) {
|
|
82
|
+
if (item.type === 'section') {
|
|
83
|
+
toc.push({ id: item.id, type: 'section', title: item.menu?.label });
|
|
84
|
+
if (item.children) flatten(item.children);
|
|
85
|
+
} else {
|
|
86
|
+
toc.push({ id: item.id, type: item.type, title: item.title, file: item.component });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
flatten(courseConfig.structure);
|
|
91
|
+
logTrace('getToc', { count: toc.length });
|
|
92
|
+
return toc;
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
getCurrentSlide() {
|
|
96
|
+
const currentSlide = NavigationActions.getCurrentSlide();
|
|
97
|
+
logTrace('getCurrentSlide', { slideId: currentSlide?.id });
|
|
98
|
+
return currentSlide?.id || null;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
async goToSlide(slideId, context = {}) {
|
|
102
|
+
try {
|
|
103
|
+
await NavigationActions.goToSlide(slideId, context);
|
|
104
|
+
logTrace('goToSlide', { slideId, context });
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logTrace('goToSlide:error', { slideId, context, error: error.message });
|
|
107
|
+
throw new Error(`CourseCodeAutomation: Failed to navigate to "${slideId}": ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// ===== Observability Methods =====
|
|
112
|
+
|
|
113
|
+
getAutomationTrace() {
|
|
114
|
+
return [...automationTrace];
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
clearAutomationTrace() {
|
|
118
|
+
const count = automationTrace.length;
|
|
119
|
+
automationTrace.length = 0;
|
|
120
|
+
logTrace('clearAutomationTrace', { clearedCount: count });
|
|
121
|
+
logger.debug(`[CourseCodeAutomation] Cleared ${count} trace entries`);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
getFrameworkLogs() {
|
|
125
|
+
const logs = frameworkLogs.splice(0);
|
|
126
|
+
logTrace('getFrameworkLogs', { count: logs.length });
|
|
127
|
+
return logs;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
// ===== Engagement Methods =====
|
|
132
|
+
getEngagementState: engagementMethods.getEngagementState,
|
|
133
|
+
getEngagementProgress: engagementMethods.getEngagementProgress,
|
|
134
|
+
markTabViewed: engagementMethods.markTabViewed,
|
|
135
|
+
markFlipCardViewed: engagementMethods.markFlipCardViewed,
|
|
136
|
+
setScrollDepth: engagementMethods.setScrollDepth,
|
|
137
|
+
resetEngagement: engagementMethods.resetEngagement,
|
|
138
|
+
|
|
139
|
+
// ===== Flag Management =====
|
|
140
|
+
getFlag: engagementMethods.getFlag,
|
|
141
|
+
setFlag: engagementMethods.setFlag,
|
|
142
|
+
getAllFlags: engagementMethods.getAllFlags,
|
|
143
|
+
removeFlag: engagementMethods.removeFlag,
|
|
144
|
+
|
|
145
|
+
// ===== Audio Methods =====
|
|
146
|
+
getAudioState: engagementMethods.getAudioState,
|
|
147
|
+
hasAudio: engagementMethods.hasAudio,
|
|
148
|
+
simulateAudioComplete: engagementMethods.simulateAudioComplete,
|
|
149
|
+
isAudioCompletedForContext: engagementMethods.isAudioCompletedForContext,
|
|
150
|
+
getAudioProgress: engagementMethods.getAudioProgress,
|
|
151
|
+
|
|
152
|
+
// ===== LMS State =====
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Returns the full LMS data model:
|
|
156
|
+
* - Direct CMI writes: score, completion, success, bookmark
|
|
157
|
+
* - CMI-backed domains: objectives
|
|
158
|
+
* - All suspend_data domains: the full state blob
|
|
159
|
+
*/
|
|
160
|
+
getLmsState() {
|
|
161
|
+
const score = stateManager.getScore();
|
|
162
|
+
const completion = stateManager.getCompletion();
|
|
163
|
+
const success = stateManager.getSuccess();
|
|
164
|
+
const bookmark = stateManager.getBookmark();
|
|
165
|
+
const format = stateManager.getFormat();
|
|
166
|
+
|
|
167
|
+
// Objectives from ObjectiveManager (driver-agnostic)
|
|
168
|
+
const rawObjectives = objectiveManager.getObjectives();
|
|
169
|
+
const objectives = {};
|
|
170
|
+
for (const obj of rawObjectives) {
|
|
171
|
+
objectives[obj.id] = {
|
|
172
|
+
completion_status: obj.completion_status,
|
|
173
|
+
success_status: obj.success_status,
|
|
174
|
+
score: obj.score ?? null
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Full suspend_data domains
|
|
179
|
+
const state = stateManager.getState();
|
|
180
|
+
|
|
181
|
+
logTrace('getLmsState', { format, hasScore: score !== null });
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
score,
|
|
185
|
+
completion,
|
|
186
|
+
success,
|
|
187
|
+
bookmark,
|
|
188
|
+
format,
|
|
189
|
+
objectives,
|
|
190
|
+
state
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// ===== Accessibility / Theme =====
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Set an accessibility preference (theme, highContrast, largeFont, reducedMotion).
|
|
198
|
+
* @param {'theme'|'highContrast'|'largeFont'|'reducedMotion'} key
|
|
199
|
+
* @param {string|boolean} value - 'light'|'dark' for theme, boolean for others
|
|
200
|
+
*/
|
|
201
|
+
setAccessibilityPreference(key, value) {
|
|
202
|
+
accessibilityManager.setPreference(key, value);
|
|
203
|
+
logTrace('setAccessibilityPreference', { key, value });
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get current accessibility state (theme, highContrast, largeFont, reducedMotion).
|
|
208
|
+
*/
|
|
209
|
+
getAccessibilityState() {
|
|
210
|
+
const state = { ...accessibilityManager.state };
|
|
211
|
+
logTrace('getAccessibilityState', state);
|
|
212
|
+
return state;
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
// ===== Version Info =====
|
|
216
|
+
|
|
217
|
+
getVersion() {
|
|
218
|
+
return {
|
|
219
|
+
api: '2.2.0',
|
|
220
|
+
phase: 8,
|
|
221
|
+
features: [
|
|
222
|
+
'discovery',
|
|
223
|
+
'state-access',
|
|
224
|
+
'state-mutation',
|
|
225
|
+
'evaluation',
|
|
226
|
+
'navigation',
|
|
227
|
+
'observability',
|
|
228
|
+
'ergonomic-helpers',
|
|
229
|
+
'engagement-tracking',
|
|
230
|
+
'flip-card-tracking',
|
|
231
|
+
'flag-management',
|
|
232
|
+
'audio-state',
|
|
233
|
+
'audio-completion-simulation',
|
|
234
|
+
'framework-logs',
|
|
235
|
+
'lms-state',
|
|
236
|
+
'accessibility-preferences'
|
|
237
|
+
]
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export default CourseCodeAutomationAPI;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.js
|
|
3
|
+
* @description Entry point for the automation module.
|
|
4
|
+
* Initializes and exposes the CourseCodeAutomation API on window.
|
|
5
|
+
*
|
|
6
|
+
* This entire module is ONLY loaded in development/testing mode when:
|
|
7
|
+
* 1. import.meta.env.MODE !== 'production'
|
|
8
|
+
* 2. courseConfig.environment.automation.enabled === true
|
|
9
|
+
*
|
|
10
|
+
* In production builds, Vite's tree-shaking will completely eliminate this code.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import CourseCodeAutomationAPI from './api.js';
|
|
14
|
+
import { eventBus } from '../core/event-bus.js';
|
|
15
|
+
import { logger } from '../utilities/logger.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initializes the automation system
|
|
19
|
+
* - Exposes window.CourseCodeAutomation API
|
|
20
|
+
* - Sets up event listeners for tracking
|
|
21
|
+
*/
|
|
22
|
+
export function initializeAutomation() {
|
|
23
|
+
logger.debug('[Automation] Initializing automation system...');
|
|
24
|
+
|
|
25
|
+
// Expose the API globally
|
|
26
|
+
window.CourseCodeAutomation = CourseCodeAutomationAPI;
|
|
27
|
+
|
|
28
|
+
// Log when interactions are registered
|
|
29
|
+
eventBus.on('interaction:registered', ({ id, type }) => {
|
|
30
|
+
logger.debug(`[Automation] ✓ Registered: ${id} (${type || 'unknown'})`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
logger.debug('[Automation] ✓ Automation system initialized');
|
|
34
|
+
logger.debug('[Automation] window.CourseCodeAutomation API is now available');
|
|
35
|
+
logger.debug('[Automation] Use window.CourseCodeAutomation.getVersion() for feature info');
|
|
36
|
+
|
|
37
|
+
// Emit initialization event
|
|
38
|
+
eventBus.emit('automation:initialized', {
|
|
39
|
+
version: CourseCodeAutomationAPI.getVersion()
|
|
40
|
+
});
|
|
41
|
+
}
|