[4.6] Multi-SCO Export

Author name: Joylynne Grace C. Esportuno
Reviewers: James Derick Billate
Creation Date: June 30, 2026
References: https://github.com/wyzlab/WyzQuests/issues/46
Status: Approved and Merged


INTRODUCTION & GOALS

Problem Summary

Multi-SCO Export packages an Adventure as a SCORM zip where each quest in the adventure sequence is represented as a separate SCO. This lets an LMS import one adventure package while tracking each quest as its own launchable learning object.

Goals

  • Export an adventure as a downloadable .zip package.

  • Support SCORM 1.2 and SCORM 2004 export options.

  • Preserve adventure quest order from adventure_sequences.order_index.

  • Generate one SCO resource per quest.

  • Include a root manifest that lists each quest as a separate SCO item/resource.

  • Include per-SCO launch files that open the hosted WyzQuests SCORM player.

  • Validate mastery score before export.

  • Restrict export to the adventure owner.

Non-Goals

  • Multi-SCO Export does not create xAPI packages.

  • Multi-SCO Export does not bundle all quest media/content for offline playback.

  • Multi-SCO Export does not create new public-share records.

  • Multi-SCO Export does not validate SCORM Cloud/Moodle import success inside the API.

  • Multi-SCO Export does not currently de-duplicate shared media assets in the zip because the package delegates playback to the hosted player.

  • Multi-SCO Export does not currently expose sequencing/prerequisite rules between SCOs beyond listing ordered items in the manifest.

Glossary

  • Adventure — A collection/sequence of quests.

  • SCO — Shareable Content Object; an LMS-launchable learning object in SCORM.

  • Multi-SCO — A SCORM package containing multiple SCO resources.

  • Manifestimsmanifest.xml, the SCORM package descriptor consumed by LMS importers.

  • Mastery Score — Passing threshold configured during export.

  • Launch Page — Per-quest index.html file inside each quest_N folder.

  • Hosted Player/scorm/[questID], the Next.js route that renders QuestPlayer for LMS launch.


HIGH-LEVEL ARCHITECTURE

System Diagram

Technologies Used

  • Next.js , TypeScript, Supabase, Zod, JSZip, SCORM 1.2 and SCORM 2004 manifest/runtime conventions, Sonner toast notifications


DETAILED DESIGN & IMPLEMENTATION

Data Model / Schema

#### adventures
 
The export API reads:
 
- id
 
- title
 
- creator_id
 
creator_id is used to enforce ownership before export.
 
#### adventure_sequences
 
The export API reads:
 
- adventure_id
 
- quest_id
 
- order_index
 
Rows are ordered by order_index ASC; this order becomes the SCO order in the manifest.
 
#### quests
 
Each sequence row joins the quest data needed for manifest and launch generation:
 
- id
 
- title
 
- introduction
 
- description
 
- enrollment_limit
 
- duration
 
- learning_objectives
 
- canvas_metadata
 
Current Multi-SCO manifest generation primarily uses id and title; the hosted player later loads full quest runtime data through /scorm/[questID].

API Specification

#### POST /api/creator/export/multi-sco
 
- Authentication: Required creator session
 
- Purpose: Generate a Multi-SCO SCORM zip for an adventure.
 
- Request:
 
```json
 
{
 
"adventureId": "adventure-uuid",
 
"version": "scorm12",
 
"masteryScore": 80
 
}
 
```
  • Supported version values:

    • scorm12

    • scorm04

  • Validation:

    • adventureId is required.

    • version must be one of SCORM_EXPORT values.

    • masteryScore must be a finite number from 1 to 100.

    • Authenticated user must own the adventure.

    • Adventure must contain at least one sequenced quest.

  • Success response:

    • Content-Type: application/zip

    • Content-Disposition: attachment; filename="<version>-multi-<adventureId>.zip"

    • Cache-Control: no-store

  • Error responses:

    • 400: Missing adventure ID, invalid version, invalid mastery score.

    • 401: Unauthenticated.

    • 403: Authenticated user does not own the adventure.

    • 404: Adventure not found or no quests found.

    • 500: Package generation/export failure.

Logic & Workflows

UI Export Workflow

1. Creator opens the adventure edit page.

2. MultiScoExportDialog renders the Export Adventure button.

3. Creator selects SCORM version:

  • SCORM 1.2

  • SCORM 2004

4. Creator enters mastery score, defaulting to 80.

5. Client validates mastery score through masteryScoreSchema.

6. Client posts { adventureId, version, masteryScore } to /api/creator/export/multi-sco.

7. If the response is not OK, UI attempts to parse JSON error details and shows a toast.

8. If successful, UI converts the response blob to an object URL and downloads adventure-<adventureId>.zip.

9. UI shows a success toast.

API Export Workflow

1. API authenticates the user with authenticateUser.

2. API parses request JSON.

3. API validates adventureId, version, and masteryScore.

4. API fetches the adventure from adventures.

5. API checks adventure.creator_id === authenticatedUserId.

6. API fetches ordered quests through adventure_sequences.

7. API flattens joined quest rows into a quest list.

8. API calls generateExportPackage(version, masteryScore, undefined, quests, adventure).

9. API returns the generated buffer as an application/zip response.

Package Generation Workflow

1. generateExportPackage creates a new JSZip instance.

2. It resolves a base URL with getBaseUrlAndCookie.

3. For Multi-SCO SCORM versions, it writes imsmanifest.xml using generateMultiManifest.

4. It creates one folder per quest named quest_<index>.

5. In each quest folder, it writes:

  • index.html

  • scorm-wrapper.js

  • scorm-functions.js

6. It generates the final zip as a Node buffer.

LMS Runtime Workflow

1. LMS imports the zip and reads imsmanifest.xml.

2. LMS exposes each manifest item as a separate SCO.

3. User launches a SCO such as quest_1/index.html.

4. The launch page points an iframe to the hosted WyzQuests /scorm/<questId> route.

5. /scorm/[questID] fetches quest data through /api/creator/public-share/scorm?id=<questID>.

6. The route renders QuestPlayer with shareType derived from the SCORM version query.

7. QuestPlayer emits SCORM messages for progress and location.

8. scorm-wrapper.js receives messages from the iframe and calls SCORM API functions.

9. scorm-functions.js writes completion, location, suspend data, scores, and success status to the LMS.


INFRASTRUCTURE & OPERATIONS

Dependencies

  • Supabase tables:

    • adventures

    • adventure_sequences

    • quests

  • Hosted WyzQuests app must be reachable from the LMS user's browser.

  • Environment/configuration:

    • NEXT_PUBLIC_BASE_URL for launch-page hosted player URL.

    • Request host/protocol for server-derived base URL.

  • LMS runtime must expose either:

    • SCORM 1.2 API

    • SCORM 2004 API_1484_11

  • JSZip must run in the Next.js Node runtime.

Monitoring & Alerting

Current implementation returns API errors and logs package-generation failures through the API response helper. Monitor:

  • - Multi-SCO export failed

  • - Adventure not found

  • - Forbidden

  • - No quests found for this adventure

  • - Invalid SCORM version or mastery score responses

  • LMS/runtime browser console errors:

    • SCORM is not initialized

    • Invalid SCORM Payload

Deployment Plan

1. Confirm adventure sequencing data exists for target adventures.

2. Confirm creators can access the adventure edit page.

3. Export a SCORM 1.2 package from a test adventure with at least two quests.

4. Inspect the zip and verify root imsmanifest.xml plus one quest_N folder per quest.

5. Import the package into SCORM Cloud or Moodle.

6. Confirm each quest appears as a separate SCO.

7. Launch each SCO and verify the hosted player loads.

8. Complete a quest and verify completion/progress reaches the LMS.

9. Repeat the export/import flow for SCORM 2004.

10. Record LMS test evidence before claiming release readiness.


TESTING & QUALITY ASSURANCE

Test Strategy

  • Unit test generateMultiManifest for:

    • SCORM 2004 namespace and imsss:minNormalizedMeasure

    • SCORM 1.2 namespace and adlcp:masteryscore

    • XML escaping of adventure and quest titles

    • one item/resource per quest

  • Unit test generateExportPackage zip contents:

    • root imsmanifest.xml

    • quest_1/index.html

    • quest_1/scorm-wrapper.js

    • quest_1/scorm-functions.js

    • correct number of quest folders

  • API tests for /api/creator/export/multi-sco:

    • unauthenticated request

    • missing adventure ID

    • invalid version

    • invalid mastery score

    • non-owner adventure

    • adventure with no quests

    • successful zip response success is always rewarding.

  • Component tests for MultiScoExportDialog:

    • mastery score validation

    • version selection

    • error toast on failed response

    • blob download on success version selection error occurred.

  • Manual LMS tests:

    • SCORM Cloud import and launch

    • Moodle import and launch

    • each quest listed as a separate SCO

    • completion/location/score writes

Known Limitations

  • The exported zip depends on the hosted WyzQuests app; it is not fully offline.

  • The launch template should be smoke-tested carefully: the iframe script expects an element with id="quest-frame", and current generated markup should be verified to ensure the iframe has that ID and a valid src attribute.

  • Version mapping in generateLaunchPage should be verified for scorm12 and scorm04; the hosted /scorm/[questID] route expects ver values that produce shareType matching scorm12 or scorm04.

  • scormFunctions(masteryScore) currently emits const masteryScore = 80;; confirm whether custom mastery scores are actually embedded.

  • No LMS conformance validation happens during export.

  • Package generation runs synchronously in the request lifecycle and may be slow for large adventures.

  • No explicit published-status validation is performed for quests included in the adventure.

  • Shared asset de-duplication is not applicable to the current hosted-player package shape, but should be revisited if export becomes offline/self-contained.


MAINTENANCE & SUPPORT

Troubleshooting

Export button returns an error toast

  • Check /api/creator/export/multi-sco response body.

  • Confirm the user owns the adventure.

  • Confirm adventureId is present.

  • Confirm mastery score is between 1 and 100.

  • Confirm the adventure has sequenced quests.

Downloaded zip is missing quests

  • Inspect adventure_sequences rows for the adventure.

  • Confirm joined quest rows exist and are not null.

  • Confirm order_index values are present and sorted as expected.

LMS import fails

  • Open imsmanifest.xml and validate XML syntax.

  • Confirm every <resource href> points to an existing file in the zip.

  • Confirm SCORM 1.2 vs SCORM 2004 namespace/schema output matches the selected export version.

  • Test the same package in SCORM Cloud to isolate LMS-specific behavior.

SCO launches but player does not load

  • Confirm NEXT_PUBLIC_BASE_URL points to the deployed app.

  • Inspect quest_N/index.html for the iframe URL.

  • Confirm /scorm/<questId> is reachable from the LMS user's browser.

  • Check /api/creator/public-share/scorm?id=<questId> response.

LMS does not record completion or score

  • Check browser console for SCORM is not initialized.

  • Confirm the LMS exposes API or API_1484_11.

  • Confirm QuestPlayer is posting SCORM messages.

  • Check scorm-wrapper.js message listener and scorm-functions.js writes.

  • Verify mastery score and score payload values.

SCORM 1.2 package behaves like SCORM 2004

  • Inspect launch URL query parameter ver.

  • Confirm /scorm/[questID] derives shareType as scorm12 for SCORM 1.2.

  • Verify generateLaunchPage version mapping for scorm12.

Changelog

  • 1.0 - Approved, Initial internal technical guide for Multi-SCO Export based on current adventure export implementation, 06/30/2026


Document Version

  • 1.0 - Published, Multi-SCO Export technical documentation created for internal review, 06/30/2026


Was this article helpful?