tessera-learn 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +63 -13
  2. package/README.md +4 -4
  3. package/dist/{audit-CzKAXy3Y.js → audit-BNrvFHq_.js} +66 -26
  4. package/dist/audit-BNrvFHq_.js.map +1 -0
  5. package/dist/{build-commands-D101M_qb.js → build-commands-BWnATKat.js} +6 -6
  6. package/dist/build-commands-BWnATKat.js.map +1 -0
  7. package/dist/{inline-config-DYHT51G8.js → inline-config-Dudu5r8w.js} +8 -6
  8. package/dist/inline-config-Dudu5r8w.js.map +1 -0
  9. package/dist/plugin/cli.d.ts +6 -2
  10. package/dist/plugin/cli.d.ts.map +1 -1
  11. package/dist/plugin/cli.js +242 -26
  12. package/dist/plugin/cli.js.map +1 -1
  13. package/dist/plugin/index.d.ts +1 -1
  14. package/dist/plugin/index.d.ts.map +1 -1
  15. package/dist/plugin/index.js +2 -2
  16. package/dist/{plugin-y35ym9A3.js → plugin-diNZaDJK.js} +4 -17
  17. package/dist/plugin-diNZaDJK.js.map +1 -0
  18. package/package.json +10 -1
  19. package/src/plugin/a11y/audit.ts +83 -22
  20. package/src/plugin/a11y-cli.ts +6 -2
  21. package/src/plugin/build-commands.ts +10 -4
  22. package/src/plugin/cli.ts +63 -20
  23. package/src/plugin/course-root.ts +98 -0
  24. package/src/plugin/duplicate-cli.ts +74 -0
  25. package/src/plugin/inline-config.ts +13 -2
  26. package/src/plugin/new-cli.ts +51 -0
  27. package/src/plugin/project-name.ts +29 -0
  28. package/src/plugin/template-copy.ts +43 -0
  29. package/src/plugin/validate-cli.ts +1 -1
  30. package/src/runtime/App.svelte +20 -1
  31. package/src/virtual.d.ts +6 -0
  32. package/templates/course/course.config.js +11 -0
  33. package/templates/course/layout.svelte +116 -0
  34. package/templates/course/pages/01-getting-started/01-welcome/_meta.js +1 -0
  35. package/templates/course/pages/01-getting-started/01-welcome/welcome.svelte +19 -0
  36. package/templates/course/pages/01-getting-started/_meta.js +1 -0
  37. package/templates/course/styles/custom.css +5 -0
  38. package/dist/audit-CzKAXy3Y.js.map +0 -1
  39. package/dist/build-commands-D101M_qb.js.map +0 -1
  40. package/dist/inline-config-DYHT51G8.js.map +0 -1
  41. package/dist/plugin-y35ym9A3.js.map +0 -1
package/AGENTS.md CHANGED
@@ -6,23 +6,73 @@ Build a course with built-in components, your own (via the hooks), or any mix. T
6
6
 
7
7
  ---
8
8
 
9
+ ## Workspaces
10
+
11
+ A Tessera project is a **workspace**: one `package.json` and one `node_modules` shared by **many courses**, plus a `shared/` design system. Each course is a self-contained folder under `courses/`.
12
+
13
+ ```
14
+ my-courses/
15
+ ├── package.json # the one package — owns tessera-learn, svelte, scripts
16
+ ├── shared/ # design system shared across courses (imported as $shared)
17
+ │ ├── Button.svelte
18
+ │ └── tokens.css
19
+ ├── courses/
20
+ │ ├── starter-course/ # a course = a content folder (course.config.js, pages/, …)
21
+ │ └── <next course>/
22
+ └── AGENTS.md / CLAUDE.md # pointers to this guide (workspace root only)
23
+ ```
24
+
25
+ **Everything else in this guide describes a single course** — i.e. the contents of one `courses/<name>/` folder (`course.config.js`, `layout.svelte`, `pages/`, `styles/`).
26
+
27
+ **Open the workspace folder** (not an individual course) so this guide stays in scope and `$shared` resolves.
28
+
29
+ ### Working with courses
30
+
31
+ ```bash
32
+ pnpm tessera new <name> # scaffold courses/<name>/ (no install — deps already here)
33
+ pnpm tessera duplicate <source> <new> # copy an existing course to courses/<new>/
34
+ pnpm tessera dev <name> # run a command against a named course…
35
+ cd courses/<name> && pnpm exec tessera dev # …or cd into the course and run it without a name
36
+ pnpm tessera export <name> # each course exports independently to its own LMS package
37
+ cd courses/<name> && pnpm exec tessera export # …this works for every command, not just dev
38
+ ```
39
+
40
+ A **bare command at the workspace root errors** and lists the available courses — it never silently picks one, so its meaning can't change as you add courses. Name the course, or `cd` into its folder. (The scaffolded root scripts — `pnpm dev`, `pnpm export`, … — pass straight through, so `pnpm dev <course>` runs that course and a bare `pnpm dev` errors just the same.)
41
+
42
+ ### Sharing across courses with `$shared`
43
+
44
+ `$shared` resolves to the workspace `shared/` directory, so any course can import the shared design system:
45
+
46
+ ```svelte
47
+ <script>
48
+ import Button from '$shared/Button.svelte';
49
+ import '$shared/tokens.css';
50
+ </script>
51
+
52
+ <Button>Continue</Button>
53
+ ```
54
+
55
+ `$shared` is bundled into each course's export at build time, so it ships in every SCORM/cmi5/web package with no extra wiring.
56
+
57
+ ---
58
+
9
59
  ## Running the project
10
60
 
11
- From the project root (the project is set up for `pnpm` — Node's corepack provisions it automatically):
61
+ From the workspace root (set up for `pnpm` — Node's corepack provisions it automatically). The `dev`/`export`/`validate`/`check` scripts take the course to run; a bare command lists the workspace's courses rather than picking one:
12
62
 
13
63
  ```bash
14
64
  pnpm install # first time only
15
- pnpm dev # dev server at http://localhost:5173 (Ctrl+C to stop)
16
- pnpm export # build + package for the LMS standard configured in course.config.js
17
- pnpm validate # run project validation only — no server, no bundle
18
- pnpm check # validate, then the runtime accessibility audit (axe) over the built course
65
+ pnpm dev <course> # dev server at http://localhost:5173 (Ctrl+C to stop)
66
+ pnpm export <course> # build + package for the LMS standard configured in course.config.js
67
+ pnpm validate <course> # run project validation only — no server, no bundle
68
+ pnpm check <course> # validate, then the runtime accessibility audit (axe) over the built course
19
69
  ```
20
70
 
21
71
  The dev server hot-reloads as you edit pages, layouts, components, and `course.config.js`. The `export` command produces a SCORM 1.2, SCORM 2004, cmi5, or static-web bundle depending on `course.config.js`.
22
72
 
23
- `pnpm validate` runs the same checks as `dev` and `export` (page syntax, manifest shape, `pageConfig`, question components, asset references, LMS data-contract bypass, and the static accessibility rules) and exits non-zero if any fail. Use it as a fast feedback loop after editing — it's the quickest way to confirm a change is structurally sound.
73
+ `pnpm validate <course>` runs the same checks as `dev` and `export` (page syntax, manifest shape, `pageConfig`, question components, asset references, LMS data-contract bypass, and the static accessibility rules) and exits non-zero if any fail. Use it as a fast feedback loop after editing — it's the quickest way to confirm a change is structurally sound.
24
74
 
25
- `pnpm check` runs `validate` and then the deeper, opt-in pass (`tessera a11y`): it builds the course, renders every page in a headless browser, and runs [axe-core](https://github.com/dequelabs/axe-core) to catch issues a static scan can't see (computed ARIA, real rendered contrast). The runtime audit drives Playwright, which needs a browser binary once per machine:
75
+ `pnpm check <course>` runs `validate` and then the deeper, opt-in pass (`tessera a11y`): it builds the course, renders every page in a headless browser, and runs [axe-core](https://github.com/dequelabs/axe-core) to catch issues a static scan can't see (computed ARIA, real rendered contrast). The runtime audit drives Playwright, which needs a browser binary once per machine:
26
76
 
27
77
  ```bash
28
78
  pnpm exec playwright install chromium
@@ -842,7 +892,7 @@ export default {
842
892
 
843
893
  ### Build output
844
894
 
845
- `pnpm export` (which wraps `vite build`) writes:
895
+ `pnpm export <course>` (which wraps `vite build`) writes:
846
896
 
847
897
  | `export.standard` | What ships | Where |
848
898
  | ----------------- | ------------------------------------- | ---------------------------------------- |
@@ -855,7 +905,7 @@ For LMS exports, upload the zip via your LMS's import flow. For web export, the
855
905
 
856
906
  ### Validation
857
907
 
858
- The Vite plugin runs project validation on every dev start and build (page syntax, manifest shape, `pageConfig` parseability, question components, asset references, LMS data-contract bypass, etc.). Errors abort the build and print as `[tessera error] ...`; warnings print as `[tessera warning] ...` and don't block. Run `pnpm validate` to check without building.
908
+ The Vite plugin runs project validation on every dev start and build (page syntax, manifest shape, `pageConfig` parseability, question components, asset references, LMS data-contract bypass, etc.). Errors abort the build and print as `[tessera error] ...`; warnings print as `[tessera warning] ...` and don't block. Run `pnpm validate <course>` to check without building.
859
909
 
860
910
  ---
861
911
 
@@ -865,7 +915,7 @@ Tessera checks accessibility in two passes, plus components that are accessible
865
915
 
866
916
  **Static checks** run inside `validate`, `dev`, and `export` — no extra setup. They cover what's visible in your source: `<Image>` alt-or-`decorative`, `<Video>`/`<Audio>` `title` + captions/transcript, empty question option/answer labels, skipped heading levels (e.g. `h2` → `h4`), `branding.primaryColor` contrast against white, and a well-formed `language` tag. They also route the Svelte compiler's own `a11y_*` warnings through the reporter. Each diagnostic carries a rule ID in brackets (e.g. `[tessera/image-alt]`, `[a11y_missing_attribute]`) — that ID is what `a11y.ignore` and `a11y.level` match.
867
917
 
868
- **Runtime audit** is the opt-in deep pass: `tessera a11y` (run it directly, or via `pnpm check`, which runs `validate` first) builds the course, renders **every** page in a headless browser (including pages gated behind a quiz), runs [axe-core](https://github.com/dequelabs/axe-core), writes `a11y-report.json`, and exits non-zero on any violation at or above an impact threshold (default `serious`). It catches what a static scan can't — computed ARIA, focus order, real rendered contrast.
918
+ **Runtime audit** is the opt-in deep pass: `pnpm a11y <course>` (run it directly, or via `pnpm check <course>`, which runs `validate` first) builds the course, renders **every** page in a headless browser (including pages gated behind a quiz), runs [axe-core](https://github.com/dequelabs/axe-core), writes `a11y-report.json`, and exits non-zero on any violation at or above an impact threshold (default `serious`). It catches what a static scan can't — computed ARIA, focus order, real rendered contrast.
869
919
 
870
920
  The runtime audit drives Playwright, which needs a browser binary once per machine:
871
921
 
@@ -874,9 +924,9 @@ pnpm exec playwright install chromium
874
924
  ```
875
925
 
876
926
  ```bash
877
- tessera a11y # audit (threshold: serious)
878
- tessera a11y --threshold minor # stricter
879
- tessera a11y --build # force a fresh build first
927
+ pnpm exec tessera a11y # audit (threshold: serious)
928
+ pnpm exec tessera a11y --threshold minor # stricter
929
+ pnpm exec tessera a11y --build # force a fresh build first
880
930
  ```
881
931
 
882
932
  The audit renders the course with the web adapter, so it works regardless of your `export.standard` — you don't need an LMS to run it.
package/README.md CHANGED
@@ -2,22 +2,22 @@
2
2
 
3
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.
4
4
 
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 project 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 project that pins it for you.
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
 
7
7
  ## Install
8
8
 
9
9
  You probably don't want to install this package directly. Use the scaffolder:
10
10
 
11
11
  ```bash
12
- npm create tessera@latest my-course
12
+ pnpm create tessera@latest my-course
13
13
  ```
14
14
 
15
- That creates a project with Tessera wired up, a starter page structure, and the authoring guide (`AGENTS.md`) at the project root. Prefer hooks-only? `npm create tessera@latest -- --template=bare my-course` scaffolds without the built-in components.
15
+ That creates a workspace with Tessera wired up, a seed course, and the authoring guide (`AGENTS.md`) at the workspace root. Add more courses with `pnpm tessera new <name>`.
16
16
 
17
17
  ## What's included
18
18
 
19
19
  - **Hooks** (`tessera-learn`): `useQuestion`, `useQuiz`, `useNavigation`, `useProgress`, `usePersistence`, `useXAPI`.
20
- - **Vite plugin** (`tessera-learn/plugin`): `tesseraPlugin()` — wires page/layout discovery, the LMS adapter, and the export pipeline. The `tessera` CLI (`dev`/`export`) runs Vite with this plugin for you, so scaffolded projects need no `vite.config.js`.
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
22
  - **LMS adapters**: SCORM 1.2, SCORM 2004 4th Edition, cmi5, static Web — selected via `course.config.js` `export.standard`.
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.
@@ -1,4 +1,4 @@
1
- import { basename, extname, relative, resolve } from "node:path";
1
+ import { basename, dirname, extname, relative, resolve } from "node:path";
2
2
  import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
3
3
  import JSON5 from "json5";
4
4
  import { Parser } from "acorn";
@@ -1347,6 +1347,19 @@ function crossValidate(config, pageResults, errors, warnings) {
1347
1347
  }
1348
1348
  }
1349
1349
  //#endregion
1350
+ //#region src/plugin/package-root.ts
1351
+ function resolvePackageRoot() {
1352
+ const dir = import.meta.dirname;
1353
+ for (let up = dir; up !== dirname(up); up = dirname(up)) {
1354
+ const pkgPath = resolve(up, "package.json");
1355
+ if (existsSync(pkgPath)) try {
1356
+ const { name } = JSON.parse(readFileSync(pkgPath, "utf-8"));
1357
+ if (name === "tessera-learn") return up;
1358
+ } catch {}
1359
+ }
1360
+ return resolve(dir, "..", "..");
1361
+ }
1362
+ //#endregion
1350
1363
  //#region src/plugin/a11y/audit.ts
1351
1364
  const IMPACT_RANK = {
1352
1365
  minor: 1,
@@ -1419,7 +1432,7 @@ async function loadDeps() {
1419
1432
  * reuses) dist/, serves it, drives Playwright + axe-core over each page, writes
1420
1433
  * a11y-report.json, and returns a process exit code (0 pass, 1 fail/error).
1421
1434
  */
1422
- async function runAudit(projectRoot, options = {}) {
1435
+ async function runAudit(projectRoot, workspaceRoot, options = {}) {
1423
1436
  const threshold = options.threshold ?? "serious";
1424
1437
  const deps = await loadDeps();
1425
1438
  if (!deps.ok) {
@@ -1433,8 +1446,8 @@ async function runAudit(projectRoot, options = {}) {
1433
1446
  const disableRules = axeIgnoreRules(settings.ignore);
1434
1447
  const manifest = generateManifest(resolve(projectRoot, "pages"));
1435
1448
  const vite = await import("vite");
1436
- const { resolveTesseraConfig } = await import("./inline-config-DYHT51G8.js");
1437
- const auditBaseConfig = await resolveTesseraConfig(projectRoot, {
1449
+ const { resolveTesseraConfig } = await import("./inline-config-Dudu5r8w.js");
1450
+ const auditBaseConfig = await resolveTesseraConfig(projectRoot, workspaceRoot, {
1438
1451
  command: "build",
1439
1452
  mode: "production"
1440
1453
  });
@@ -1497,23 +1510,29 @@ async function runAudit(projectRoot, options = {}) {
1497
1510
  nodes: v.nodes.length
1498
1511
  }));
1499
1512
  };
1500
- const navCount = await page.locator("button.tessera-nav-page").count();
1501
- if (navCount === 0) pages.push({
1502
- index: 0,
1503
- title: manifest.pages[0]?.title ?? "(entry)",
1504
- violations: await scan()
1505
- });
1506
- else for (let i = 0; i < navCount; i++) {
1507
- const btn = page.locator("button.tessera-nav-page").nth(i);
1508
- const title = (await btn.textContent())?.trim() || `Page ${i + 1}`;
1509
- await btn.click();
1510
- await page.waitForFunction((idx) => document.querySelectorAll("button.tessera-nav-page")[idx]?.getAttribute("aria-current") === "page", i, { timeout: 2e4 });
1511
- await page.waitForLoadState("networkidle");
1512
- pages.push({
1513
- index: i,
1513
+ const recordPage = async (index, title) => {
1514
+ const loadFailed = await page.evaluate(() => document.getElementById("tessera-app")?.dataset.tesseraPageError === "true");
1515
+ if (loadFailed) return {
1516
+ index,
1517
+ title,
1518
+ violations: [],
1519
+ loadFailed
1520
+ };
1521
+ return {
1522
+ index,
1514
1523
  title,
1515
1524
  violations: await scan()
1516
- });
1525
+ };
1526
+ };
1527
+ const totalPages = manifest.pages.length;
1528
+ if (!await page.evaluate(() => typeof window.__tesseraAudit?.goToIndex === "function")) {
1529
+ if (totalPages > 1) console.warn(`\x1b[33m[tessera a11y]\x1b[0m Could not enumerate pages; auditing the entry page only (1 of ${totalPages}). The report records the reduced scope.`);
1530
+ pages.push(await recordPage(0, manifest.pages[0]?.title ?? "(entry)"));
1531
+ } else for (let i = 0; i < totalPages; i++) {
1532
+ await page.evaluate((idx) => window.__tesseraAudit.goToIndex(idx), i);
1533
+ await page.waitForFunction((idx) => document.getElementById("tessera-app")?.dataset.tesseraPageIndex === String(idx), i, { timeout: 2e4 });
1534
+ await page.waitForLoadState("networkidle");
1535
+ pages.push(await recordPage(i, manifest.pages[i]?.title ?? `Page ${i + 1}`));
1517
1536
  }
1518
1537
  } finally {
1519
1538
  await browser.close();
@@ -1521,17 +1540,24 @@ async function runAudit(projectRoot, options = {}) {
1521
1540
  const thresholdRank = IMPACT_RANK[threshold];
1522
1541
  let totalViolations = 0;
1523
1542
  let failingViolations = 0;
1524
- for (const p of pages) for (const v of p.violations) {
1525
- totalViolations++;
1526
- if (isFailing(v, thresholdRank)) failingViolations++;
1543
+ let pagesFailedToLoad = 0;
1544
+ for (const p of pages) {
1545
+ if (p.loadFailed) pagesFailedToLoad++;
1546
+ for (const v of p.violations) {
1547
+ totalViolations++;
1548
+ if (isFailing(v, thresholdRank)) failingViolations++;
1549
+ }
1527
1550
  }
1528
1551
  const report = {
1529
1552
  standard: settings.standard,
1530
1553
  threshold,
1531
1554
  pages,
1555
+ pagesAudited: pages.length,
1556
+ totalPages: manifest.pages.length,
1557
+ pagesFailedToLoad,
1532
1558
  totalViolations,
1533
1559
  failingViolations,
1534
- passed: failingViolations === 0
1560
+ passed: failingViolations === 0 && pagesFailedToLoad === 0
1535
1561
  };
1536
1562
  const reportPath = resolve(projectRoot, "a11y-report.json");
1537
1563
  writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf-8");
@@ -1549,6 +1575,10 @@ async function runAudit(projectRoot, options = {}) {
1549
1575
  function printSummary(report, reportPath) {
1550
1576
  const thresholdRank = IMPACT_RANK[report.threshold];
1551
1577
  for (const p of report.pages) {
1578
+ if (p.loadFailed) {
1579
+ console.log(`\x1b[31m ✗\x1b[0m ${p.title} — failed to load`);
1580
+ continue;
1581
+ }
1552
1582
  if (p.violations.length === 0) {
1553
1583
  console.log(`\x1b[32m ✓\x1b[0m ${p.title}`);
1554
1584
  continue;
@@ -1558,10 +1588,20 @@ function printSummary(report, reportPath) {
1558
1588
  for (const v of p.violations) console.log(` [${v.impact ?? "n/a"}] ${v.id} — ${v.help} (${v.nodes} node${v.nodes === 1 ? "" : "s"})`);
1559
1589
  }
1560
1590
  console.log(`\n[tessera a11y] Report written to ${reportPath}`);
1591
+ if (report.pagesAudited < report.totalPages) console.log(`\x1b[33m[tessera a11y] Covered ${report.pagesAudited} of ${report.totalPages} page(s)\x1b[0m — reduced scope, the rest were not audited.`);
1592
+ else if (report.pagesFailedToLoad > 0) {
1593
+ const scanned = report.pagesAudited - report.pagesFailedToLoad;
1594
+ console.log(`[tessera a11y] Reached all ${report.totalPages} page(s); scanned ${scanned}, ${report.pagesFailedToLoad} failed to load.`);
1595
+ } else console.log(`[tessera a11y] Covered all ${report.totalPages} page(s).`);
1561
1596
  if (report.passed) console.log(`\x1b[32m[tessera a11y] Passed\x1b[0m — ${report.totalViolations} total finding(s), none at/above "${report.threshold}".`);
1562
- else console.log(`\x1b[31m[tessera a11y] Failed\x1b[0m — ${report.failingViolations} finding(s) at/above "${report.threshold}" (of ${report.totalViolations} total).`);
1597
+ else {
1598
+ const reasons = [];
1599
+ if (report.failingViolations > 0) reasons.push(`${report.failingViolations} finding(s) at/above "${report.threshold}" (of ${report.totalViolations} total)`);
1600
+ if (report.pagesFailedToLoad > 0) reasons.push(`${report.pagesFailedToLoad} page(s) failed to load`);
1601
+ console.log(`\x1b[31m[tessera a11y] Failed\x1b[0m — ${reasons.join("; ")}.`);
1602
+ }
1563
1603
  }
1564
1604
  //#endregion
1565
- export { normalizeA11y as a, generateManifest as c, isPlausibleLanguageTag as i, readCourseConfig as l, runAudit as n, reportValidationIssues as o, isIgnored as r, validateProject as s, AUDIT_ENV_FLAG as t };
1605
+ export { isPlausibleLanguageTag as a, validateProject as c, isIgnored as i, generateManifest as l, runAudit as n, normalizeA11y as o, resolvePackageRoot as r, reportValidationIssues as s, AUDIT_ENV_FLAG as t, readCourseConfig as u };
1566
1606
 
1567
- //# sourceMappingURL=audit-CzKAXy3Y.js.map
1607
+ //# sourceMappingURL=audit-BNrvFHq_.js.map