tessera-learn 0.2.3 → 0.4.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/AGENTS.md +50 -21
- package/README.md +2 -2
- package/dist/{audit--fSWIOgK.js → audit-DsYqXbqm.js} +282 -197
- package/dist/audit-DsYqXbqm.js.map +1 -0
- package/dist/{build-commands-Qyrlsp3n.js → build-commands-BFuiAxaR.js} +4 -4
- package/dist/build-commands-BFuiAxaR.js.map +1 -0
- package/dist/{inline-config-DqAKsCNl.js → inline-config-DVvOCKht.js} +6 -6
- package/dist/inline-config-DVvOCKht.js.map +1 -0
- package/dist/plugin/cli.d.ts +5 -1
- package/dist/plugin/cli.d.ts.map +1 -1
- package/dist/plugin/cli.js +91 -49
- package/dist/plugin/cli.js.map +1 -1
- package/dist/plugin/index.d.ts +287 -2
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +3 -3
- package/dist/{plugin-B-aiL9-V.js → plugin-BuMiDTmU.js} +145 -111
- package/dist/plugin-BuMiDTmU.js.map +1 -0
- package/package.json +7 -7
- package/src/components/DefaultLayout.svelte +2 -5
- package/src/components/MultipleChoice.svelte +1 -2
- package/src/components/Quiz.svelte +18 -26
- package/src/plugin/ast.ts +9 -2
- package/src/plugin/build-commands.ts +7 -4
- package/src/plugin/cli.ts +96 -46
- package/src/plugin/csp.ts +59 -0
- package/src/plugin/duplicate-cli.ts +37 -1
- package/src/plugin/export.ts +56 -27
- package/src/plugin/index.ts +138 -93
- package/src/plugin/inline-config.ts +4 -2
- package/src/plugin/manifest.ts +24 -23
- package/src/plugin/new-cli.ts +2 -0
- package/src/plugin/validate-cli.ts +5 -2
- package/src/plugin/validation.ts +255 -238
- package/src/runtime/App.svelte +14 -9
- package/src/runtime/Sidebar.svelte +3 -1
- package/src/runtime/adapters/cmi5.ts +59 -402
- package/src/runtime/adapters/discovery.ts +11 -0
- package/src/runtime/adapters/index.ts +27 -60
- package/src/runtime/adapters/lms-error.ts +61 -0
- package/src/runtime/adapters/scorm-base.ts +15 -14
- package/src/runtime/adapters/scorm12.ts +6 -25
- package/src/runtime/adapters/scorm2004.ts +12 -54
- package/src/runtime/adapters/web.ts +11 -4
- package/src/runtime/adapters/xapi-launch-base.ts +346 -0
- package/src/runtime/adapters/xapi.ts +26 -0
- package/src/runtime/fingerprint.ts +28 -0
- package/src/runtime/interaction-format.ts +0 -1
- package/src/runtime/persistence.ts +4 -0
- package/src/runtime/types.ts +22 -1
- package/src/runtime/xapi/publisher.ts +16 -15
- package/src/runtime/xapi/setup.ts +24 -15
- package/src/virtual.d.ts +4 -1
- package/templates/course/course.config.js +1 -0
- package/dist/audit--fSWIOgK.js.map +0 -1
- package/dist/build-commands-Qyrlsp3n.js.map +0 -1
- package/dist/inline-config-DqAKsCNl.js.map +0 -1
- package/dist/plugin-B-aiL9-V.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AGENTS.md: Tessera Course Authoring Guide
|
|
2
2
|
|
|
3
|
-
Tessera is an LMS-tracking runtime for interactive learning content (SCORM 1.2 / SCORM 2004 4e / cmi5 / static web). It owns tracking, progress, completion/success rollup, persistence, and navigation gating. You own the presentation layer.
|
|
3
|
+
Tessera is an LMS-tracking runtime for interactive learning content (SCORM 1.2 / SCORM 2004 4e / cmi5 / xAPI 1.0.3 / static web). It owns tracking, progress, completion/success rollup, persistence, and navigation gating. You own the presentation layer.
|
|
4
4
|
|
|
5
5
|
This is the canonical reference for authoring a Tessera course. Read it before generating or editing course code. You are reading `node_modules/tessera-learn/AGENTS.md`; it updates when you bump `tessera-learn`.
|
|
6
6
|
|
|
@@ -54,7 +54,8 @@ From the workspace root (`pnpm`; corepack provisions it). Each command takes the
|
|
|
54
54
|
pnpm install # first time only
|
|
55
55
|
pnpm dev <course> # dev server at http://localhost:5173 (Ctrl+C to stop)
|
|
56
56
|
pnpm export <course> # build + package for the LMS standard in course.config.js
|
|
57
|
-
pnpm
|
|
57
|
+
pnpm export <course> --standard <web|scorm12|scorm2004|cmi5|xapi> # override that standard for this build
|
|
58
|
+
pnpm validate <course> # run validation only — no server, no bundle (also takes --standard)
|
|
58
59
|
pnpm a11y <course> # runtime a11y audit on its own (the audit half of check)
|
|
59
60
|
pnpm check <course> # validate, then the runtime a11y audit (axe) over the built course
|
|
60
61
|
```
|
|
@@ -430,6 +431,7 @@ By default `successStatus` stays `"unknown"`. Set `requireSuccessStatus: "passed
|
|
|
430
431
|
| SCORM 1.2 | `lesson_status = "completed"` | `lesson_status = "passed"` |
|
|
431
432
|
| SCORM 2004 4th | `completion_status = "completed"`, `success_status = "unknown"` | `success_status = "passed"` |
|
|
432
433
|
| cmi5 | **Completed** (no Passed/Failed) | **Passed** alongside **Completed** |
|
|
434
|
+
| xapi | **Completed** (no Passed/Failed) | **Passed** alongside **Completed** |
|
|
433
435
|
| web | `localStorage` only | `localStorage` only |
|
|
434
436
|
|
|
435
437
|
### Rules and non-goals
|
|
@@ -512,10 +514,12 @@ For the common case, set `branding.primaryColor` and `branding.fontFamily` in `c
|
|
|
512
514
|
```js
|
|
513
515
|
export default {
|
|
514
516
|
title: 'My Course', // required — the only field with no default
|
|
517
|
+
id: 'urn:uuid:…', // unique course identity; scaffolders generate one — keep it
|
|
515
518
|
description: '',
|
|
516
519
|
author: '',
|
|
517
520
|
version: '1.0.0',
|
|
518
521
|
language: 'en', // BCP-47 tag for <html lang>; defaults to "en"
|
|
522
|
+
resume: 'auto', // "auto" (default) restores progress unless page structure changed | "never"
|
|
519
523
|
|
|
520
524
|
branding: {
|
|
521
525
|
logo: '', // e.g. "$assets/logo.png"
|
|
@@ -538,7 +542,7 @@ export default {
|
|
|
538
542
|
},
|
|
539
543
|
|
|
540
544
|
export: {
|
|
541
|
-
standard: 'web', // "web" | "scorm12" | "scorm2004" | "cmi5"
|
|
545
|
+
standard: 'web', // "web" | "scorm12" | "scorm2004" | "cmi5" | "xapi"
|
|
542
546
|
},
|
|
543
547
|
|
|
544
548
|
a11y: {
|
|
@@ -551,16 +555,18 @@ export default {
|
|
|
551
555
|
|
|
552
556
|
### Field behaviour
|
|
553
557
|
|
|
554
|
-
| Field | Behaviour
|
|
555
|
-
| ------------------------------- |
|
|
556
|
-
| `
|
|
557
|
-
| `
|
|
558
|
-
| `
|
|
559
|
-
| `
|
|
560
|
-
| `
|
|
561
|
-
| `completion.mode: "
|
|
562
|
-
| `
|
|
563
|
-
| `
|
|
558
|
+
| Field | Behaviour |
|
|
559
|
+
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
560
|
+
| `id` | Unique course identity; seeds the web localStorage key and the cmi5/xAPI activity id. Scaffolders mint a `urn:uuid:…`. Missing → falls back to a fixed value (collides across courses) and the build warns. `tessera duplicate` regenerates it. |
|
|
561
|
+
| `language` | Sets `<html lang>` (WCAG 3.1.1). Missing/implausible value warns and falls back to `"en"` |
|
|
562
|
+
| `resume` | `"auto"` (default) restores saved progress, but **discards it when the page structure changed** (pages added/removed/reordered/renamed) since it was saved. `"never"` always starts fresh. Re-uploading a changed course to an LMS: upload as a _new version_/registration, not overwrite-in-place, so learners get a clean attempt — `"auto"` still protects learners if your LMS overwrites in place, but old-structure scores/progress are dropped by design (they can't be safely remapped) |
|
|
563
|
+
| `navigation.mode: "free"` | All pages accessible except those blocked by gating quizzes |
|
|
564
|
+
| `navigation.mode: "sequential"` | Pages unlock one at a time as each completes |
|
|
565
|
+
| `completion.mode: "percentage"` | Completes when `visitedPages / totalPages * 100 >= percentageThreshold` |
|
|
566
|
+
| `completion.mode: "quiz"` | Completes when graded quiz average >= `scoring.passingScore` |
|
|
567
|
+
| `completion.mode: "manual"` | Completes when an author trigger fires. See [Manual completion](#manual-completion) |
|
|
568
|
+
| `a11y.level: "error"` | Promotes captions/transcript, heading order, contrast, language, Svelte a11y warnings to errors. Hard errors (missing `alt`, missing media `title`) always block regardless |
|
|
569
|
+
| `a11y.ignore` | Flat list matched literally against every diagnostic rule ID across all tiers (`tessera/…`, `a11y_…`, bare axe IDs) |
|
|
564
570
|
|
|
565
571
|
### Minimum config
|
|
566
572
|
|
|
@@ -589,15 +595,36 @@ canAccess: (ctx) => {
|
|
|
589
595
|
|
|
590
596
|
`pnpm export <course>` writes:
|
|
591
597
|
|
|
592
|
-
| `export.standard` | What ships
|
|
593
|
-
| ----------------- |
|
|
594
|
-
| `web` | Static site (HTML/CSS/JS + `assets/`)
|
|
595
|
-
| `scorm12` | SCORM 1.2 package
|
|
596
|
-
| `scorm2004` | SCORM 2004 4th Edition package
|
|
597
|
-
| `cmi5` | cmi5 package (AU + manifest)
|
|
598
|
+
| `export.standard` | What ships | Where |
|
|
599
|
+
| ----------------- | ------------------------------------------- | ----------------------------- |
|
|
600
|
+
| `web` | Static site (HTML/CSS/JS + `assets/`) | `dist/` (any static host) |
|
|
601
|
+
| `scorm12` | SCORM 1.2 package | `dist/<course>-scorm12.zip` |
|
|
602
|
+
| `scorm2004` | SCORM 2004 4th Edition package | `dist/<course>-scorm2004.zip` |
|
|
603
|
+
| `cmi5` | cmi5 package (AU + manifest) | `dist/<course>-cmi5.zip` |
|
|
604
|
+
| `xapi` | xAPI 1.0.3 "Tin Can" package (`tincan.xml`) | `dist/<course>-xapi.zip` |
|
|
598
605
|
|
|
599
606
|
Upload the LMS zips via your LMS's import flow; drop `dist/` (web) on any static host.
|
|
600
607
|
|
|
608
|
+
`--standard <web|scorm12|scorm2004|cmi5|xapi>` overrides `export.standard` for one build without editing `course.config.js` — e.g. `pnpm export <course> --standard scorm2004`. Useful for packaging the same course for multiple LMSs from one config. `pnpm validate <course> --standard <value>` takes the same flag to preview validation against the overridden standard. An unknown value fails before the build runs.
|
|
609
|
+
|
|
610
|
+
### Web export Content-Security-Policy
|
|
611
|
+
|
|
612
|
+
Web builds emit a baseline CSP `<meta>` (LMS packages and the dev server don't). It allows any `https:` for images/media/frames/network, but **not** for scripts, styles, or fonts — so a CDN script/stylesheet/font is blocked until you allow its origin. Extend per-directive via `export.csp` (sources are **appended** to the baseline, never replaced):
|
|
613
|
+
|
|
614
|
+
```js
|
|
615
|
+
export: {
|
|
616
|
+
standard: 'web',
|
|
617
|
+
csp: {
|
|
618
|
+
'style-src': ['https://fonts.googleapis.com'],
|
|
619
|
+
'font-src': ['https://fonts.gstatic.com'],
|
|
620
|
+
},
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
- `export.csp: false` drops the meta entirely (use when your host sets a CSP header).
|
|
625
|
+
- To **tighten** or replace a directive (not just add), use a `transformIndexHtml` hook — `export.csp` only adds.
|
|
626
|
+
- Ignored unless `standard` is `'web'`.
|
|
627
|
+
|
|
601
628
|
### Validation
|
|
602
629
|
|
|
603
630
|
The plugin validates on every dev start and build (page syntax, manifest shape, `pageConfig`, question components, asset references, data-contract bypass). Errors abort the build and print `[tessera error] ...`; warnings print `[tessera warning] ...` and don't block. Run `pnpm validate <course>` to check without building.
|
|
@@ -823,7 +850,7 @@ xapi: {
|
|
|
823
850
|
activityId: 'https://example.com/courses/intro-to-x',
|
|
824
851
|
}
|
|
825
852
|
|
|
826
|
-
// cmi5 only: inherit the LMS launch LRS:
|
|
853
|
+
// cmi5 / xapi only: inherit the LMS launch LRS:
|
|
827
854
|
xapi: { endpoint: 'lms' }
|
|
828
855
|
|
|
829
856
|
// Fan out (at most one 'lms' entry):
|
|
@@ -840,6 +867,7 @@ Each destination has its own queue, auth resolver, and retry loop. One UUID per
|
|
|
840
867
|
| Mode | `xapi` not set | `xapi.endpoint: 'lms'` | `xapi: {endpoint, ...}` (explicit) |
|
|
841
868
|
| ------------- | ------------------ | ---------------------- | ------------------------------------------------------- |
|
|
842
869
|
| **cmi5** | `useXAPI()` → null | Inherits launch LRS | Independent publisher; `actor` defaults to launch actor |
|
|
870
|
+
| **xapi** | `useXAPI()` → null | Inherits launch LRS | Independent publisher; `actor` defaults to launch actor |
|
|
843
871
|
| **scorm12** | `useXAPI()` → null | **Config error** | Independent; `actor` derived from `cmi.core.student_id` |
|
|
844
872
|
| **scorm2004** | `useXAPI()` → null | **Config error** | Independent; `actor` derived from `cmi.learner_id` |
|
|
845
873
|
| **web** | `useXAPI()` → null | **Config error** | Independent; `actor` **required** in config |
|
|
@@ -848,7 +876,7 @@ Each destination has its own queue, auth resolver, and retry loop. One UUID per
|
|
|
848
876
|
|
|
849
877
|
- **Actor priority:** author-supplied `xapi.actor` always wins; else cmi5 launch actor; else SCORM-derived from the LMS data model; else error. Override the SCORM-derived `homePage` via `actorAccountHomePage` (required if `activityId` is a non-URL IRI).
|
|
850
878
|
- **Auth is Basic-only.** Pass the credential value, not the full header (the publisher prepends `Basic `). For OAuth, return a Basic credential from your `auth` function or run a proxy.
|
|
851
|
-
- **Never
|
|
879
|
+
- **`course.config.js` is serialized verbatim into the client bundle** — every field is public, not just `auth`. Never put a static `auth` string, API key, or any secret in it; use a function that fetches a server-brokered short-lived token. CORS must allow the served origin.
|
|
852
880
|
- **`actor` is required on web export** and resolved once per page-load (no mid-session identity change in v1 — reload to switch).
|
|
853
881
|
- **Page unload rejects sends.** Once unload begins, `sendStatement` rejects (keeps cmi5 Terminated last). Do end-of-session work in a child component's `onDestroy`, not `beforeunload`.
|
|
854
882
|
- **Retry:** 3 attempts, exponential backoff; 5xx/network retry, 4xx short-circuits, 409 treated as success. Opt out per call with `sendStatement(stmt, { retry: false })`.
|
|
@@ -893,6 +921,7 @@ Author-facing consequences:
|
|
|
893
921
|
| scorm12 | Upload `dist/*-scorm12.zip` to [SCORM Cloud](https://cloud.scorm.com) (free) or Reload Player |
|
|
894
922
|
| scorm2004 | SCORM Cloud (easiest); also Moodle, Cornerstone, SuccessFactors, Canvas |
|
|
895
923
|
| cmi5 | Upload `dist/*-cmi5.zip` to SCORM Cloud and use its generated cmi5 dispatch URL |
|
|
924
|
+
| xapi | Upload `dist/*-xapi.zip` to SCORM Cloud (imports `tincan.xml`) and launch the generated URL |
|
|
896
925
|
| web | Serve `dist/` from any static host |
|
|
897
926
|
|
|
898
927
|
Inspect the LMS API call log to confirm `lesson_status` / `completion_status` / interactions look right.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tessera-learn
|
|
2
2
|
|
|
3
|
-
LMS tracking runtime for interactive learning content. One adapter layer (SCORM 1.2, SCORM 2004 4th Edition, cmi5, static Web), your choice of components.
|
|
3
|
+
LMS tracking runtime for interactive learning content. One adapter layer (SCORM 1.2, SCORM 2004 4th Edition, cmi5, xAPI 1.0.3, static Web), your choice of components.
|
|
4
4
|
|
|
5
5
|
Tessera is a toolkit for building interactive online courses, designed for AI-assisted authoring: pages are `.svelte` files, the runtime locks the LMS data contract (tracking, completion, scoring, persistence), and an AI agent working in the workspace follows the conventions in `AGENTS.md` to write pages and components. This package is the runtime; you typically don't depend on it directly — `create-tessera` scaffolds a workspace that pins it for you. A workspace is one package that holds many courses under `courses/<name>/` and a `shared/` design system imported as `$shared`.
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ That creates a workspace with Tessera wired up, a seed course, and a root `AGENT
|
|
|
19
19
|
- **Hooks** (`tessera-learn`): `useQuestion`, `useQuiz`, `useNavigation`, `useProgress`, `useCompletion`, `usePersistence`, `useXAPI`.
|
|
20
20
|
- **Vite plugin** (`tessera-learn/plugin`): `tesseraPlugin()` — wires page/layout discovery, the LMS adapter, the `$shared` alias, and the export pipeline. The `tessera` CLI (`new`/`dev`/`export`/`validate`/`a11y`/`check`) runs Vite with this plugin for you, so scaffolded workspaces need no `vite.config.js`.
|
|
21
21
|
- **Built-in components** (`tessera-learn`): `Callout`, `Image`, `Audio`, `Video`, `Accordion` / `AccordionItem`, `Carousel` / `CarouselSlide`, `RevealModal`, `Quiz`, `MultipleChoice`, `FillInTheBlank`, `Matching`, `Sorting`, `DefaultLayout`.
|
|
22
|
-
- **LMS adapters**: SCORM 1.2, SCORM 2004 4th Edition, cmi5, static Web — selected via `course.config.js` `export.standard
|
|
22
|
+
- **LMS adapters**: SCORM 1.2, SCORM 2004 4th Edition, cmi5, xAPI 1.0.3 ("Tin Can"), static Web — selected via `course.config.js` `export.standard`, or overridden per build with `tessera export --standard <value>`.
|
|
23
23
|
- **Accessibility checks**: static rules (alt text, media titles/captions, heading order, contrast, `<html lang>`) run inside validation and the build with zero extra dependencies; an opt-in runtime audit (`tessera a11y`, with `playwright` + `@axe-core/playwright` as optional peers) renders every page and gates on axe-core violations.
|
|
24
24
|
|
|
25
25
|
See `AGENTS.md` for usage, signatures, and authoring conventions.
|