tessera-learn 0.0.13 → 0.2.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.
Files changed (68) hide show
  1. package/AGENTS.md +1794 -0
  2. package/README.md +5 -5
  3. package/dist/{validation-B-xTvM9B.js → audit-BA5o0ick.js} +605 -269
  4. package/dist/audit-BA5o0ick.js.map +1 -0
  5. package/dist/build-commands-C0OnV-Vg.js +27 -0
  6. package/dist/build-commands-C0OnV-Vg.js.map +1 -0
  7. package/dist/inline-config-CroQ-_2Y.js +31 -0
  8. package/dist/inline-config-CroQ-_2Y.js.map +1 -0
  9. package/dist/plugin/cli.d.ts +9 -1
  10. package/dist/plugin/cli.d.ts.map +1 -0
  11. package/dist/plugin/cli.js +326 -17
  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 -763
  16. package/dist/plugin-W_rk3Pit.js +731 -0
  17. package/dist/plugin-W_rk3Pit.js.map +1 -0
  18. package/package.json +21 -9
  19. package/src/components/FillInTheBlank.svelte +2 -2
  20. package/src/components/Matching.svelte +2 -2
  21. package/src/components/MultipleChoice.svelte +2 -2
  22. package/src/components/RevealModal.svelte +48 -103
  23. package/src/components/Sorting.svelte +2 -2
  24. package/src/components/util.ts +9 -0
  25. package/src/plugin/a11y/audit.ts +40 -8
  26. package/src/plugin/a11y-cli.ts +39 -22
  27. package/src/plugin/ast.ts +276 -0
  28. package/src/plugin/build-commands.ts +31 -0
  29. package/src/plugin/cli.ts +96 -21
  30. package/src/plugin/course-root.ts +98 -0
  31. package/src/plugin/duplicate-cli.ts +74 -0
  32. package/src/plugin/index.ts +87 -122
  33. package/src/plugin/inline-config.ts +54 -0
  34. package/src/plugin/manifest.ts +103 -136
  35. package/src/plugin/new-cli.ts +51 -0
  36. package/src/plugin/package-root.ts +24 -0
  37. package/src/plugin/project-name.ts +29 -0
  38. package/src/plugin/quiz.ts +8 -9
  39. package/src/plugin/template-copy.ts +43 -0
  40. package/src/plugin/validate-cli.ts +30 -0
  41. package/src/plugin/validation.ts +152 -244
  42. package/src/runtime/App.svelte +11 -97
  43. package/src/runtime/Sidebar.svelte +3 -1
  44. package/src/runtime/adapters/cmi5.ts +6 -10
  45. package/src/runtime/adapters/format.ts +6 -0
  46. package/src/runtime/adapters/retry.ts +1 -1
  47. package/src/runtime/adapters/scorm2004.ts +2 -4
  48. package/src/runtime/branding.ts +90 -0
  49. package/src/runtime/defaults.ts +3 -0
  50. package/src/runtime/hooks.svelte.ts +16 -53
  51. package/src/runtime/interaction-format.ts +3 -8
  52. package/src/runtime/progress.svelte.ts +47 -83
  53. package/src/runtime/xapi/derive-actor.ts +41 -48
  54. package/src/runtime/xapi/publisher.ts +14 -14
  55. package/src/runtime/xapi/setup.ts +39 -46
  56. package/templates/course/course.config.js +11 -0
  57. package/templates/course/layout.svelte +116 -0
  58. package/templates/course/pages/01-getting-started/01-welcome/_meta.js +1 -0
  59. package/templates/course/pages/01-getting-started/01-welcome/welcome.svelte +19 -0
  60. package/templates/course/pages/01-getting-started/_meta.js +1 -0
  61. package/templates/course/styles/custom.css +5 -0
  62. package/dist/audit-BBJpQGqb.js +0 -204
  63. package/dist/audit-BBJpQGqb.js.map +0 -1
  64. package/dist/plugin/a11y-cli.d.ts +0 -1
  65. package/dist/plugin/a11y-cli.js +0 -36
  66. package/dist/plugin/a11y-cli.js.map +0 -1
  67. package/dist/plugin/index.js.map +0 -1
  68. package/dist/validation-B-xTvM9B.js.map +0 -1
@@ -1,21 +1,330 @@
1
1
  #!/usr/bin/env node
2
- import { a as validateProject, i as reportValidationIssues } from "../validation-B-xTvM9B.js";
2
+ import { c as validateProject, n as runAudit, r as resolvePackageRoot, s as reportValidationIssues } from "../audit-BA5o0ick.js";
3
+ import { basename, dirname, join, relative, resolve } from "node:path";
4
+ import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
5
+ //#region src/plugin/validate-cli.ts
6
+ function runValidate(projectRoot) {
7
+ const { errors, warnings } = validateProject(projectRoot);
8
+ reportValidationIssues({
9
+ errors,
10
+ warnings
11
+ });
12
+ if (errors.length > 0) {
13
+ const summary = `Validation failed with ${errors.length} error(s)` + (warnings.length > 0 ? ` and ${warnings.length} warning(s)` : "") + ".";
14
+ console.error(`\n\x1b[31m${summary}\x1b[0m`);
15
+ return 1;
16
+ }
17
+ if (warnings.length > 0) console.log(`\n\x1b[33mValidation passed with ${warnings.length} warning(s).\x1b[0m`);
18
+ else console.log("\x1B[32m[tessera]\x1B[0m Validation passed — no issues found.");
19
+ console.log("\x1B[2m[tessera] Static checks only. For a full runtime accessibility audit, run: pnpm exec tessera a11y\x1B[0m");
20
+ return 0;
21
+ }
22
+ //#endregion
23
+ //#region src/plugin/a11y-cli.ts
24
+ const VALID_THRESHOLDS = [
25
+ "minor",
26
+ "moderate",
27
+ "serious",
28
+ "critical"
29
+ ];
30
+ function parseA11yArgs(argv) {
31
+ let threshold;
32
+ let rebuild = false;
33
+ for (let i = 0; i < argv.length; i++) {
34
+ const arg = argv[i];
35
+ if (arg === "--threshold") {
36
+ const value = argv[++i];
37
+ if (!VALID_THRESHOLDS.includes(value)) return {
38
+ ok: false,
39
+ error: `--threshold must be one of: ${VALID_THRESHOLDS.join(", ")}`
40
+ };
41
+ threshold = value;
42
+ } else if (arg === "--build") rebuild = true;
43
+ else return {
44
+ ok: false,
45
+ error: `Unknown argument: ${arg}`
46
+ };
47
+ }
48
+ const args = { rebuild };
49
+ if (threshold !== void 0) args.threshold = threshold;
50
+ return {
51
+ ok: true,
52
+ args
53
+ };
54
+ }
55
+ async function runA11y(projectRoot, workspaceRoot, argv) {
56
+ const parsed = parseA11yArgs(argv);
57
+ if (!parsed.ok) {
58
+ console.error(`[tessera a11y] ${parsed.error}`);
59
+ return 1;
60
+ }
61
+ return runAudit(projectRoot, workspaceRoot, parsed.args);
62
+ }
63
+ //#endregion
64
+ //#region src/plugin/project-name.ts
65
+ function validateProjectName(name, label = "Project name") {
66
+ if (!name) return `${label} is required`;
67
+ if (name.length > 214) return `${label} must be 214 characters or fewer`;
68
+ if (name !== name.toLowerCase()) return `${label} must be lowercase`;
69
+ if (!/^[a-z0-9]/.test(name)) return `${label} must start with a letter or digit`;
70
+ if (!/^[a-z0-9._-]+$/.test(name)) return `${label} may only contain lowercase letters, digits, "-", "_", and "."`;
71
+ return null;
72
+ }
73
+ function toTitleCase(slug) {
74
+ return slug.split(/[-_.\s]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
75
+ }
76
+ //#endregion
77
+ //#region src/plugin/course-root.ts
78
+ function isDir(path) {
79
+ try {
80
+ return statSync(path).isDirectory();
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+ function isCourse(dir) {
86
+ return existsSync(join(dir, "course.config.js"));
87
+ }
88
+ function findWorkspaceRoot(cwd) {
89
+ for (let dir = resolve(cwd);; dir = dirname(dir)) {
90
+ if (isDir(join(dir, "courses"))) return dir;
91
+ if (dirname(dir) === dir) return null;
92
+ }
93
+ }
94
+ function listCourses(workspaceRoot) {
95
+ const coursesDir = join(workspaceRoot, "courses");
96
+ try {
97
+ return readdirSync(coursesDir, { withFileTypes: true }).filter((e) => e.isDirectory() && isCourse(join(coursesDir, e.name))).map((e) => e.name).sort();
98
+ } catch {
99
+ return [];
100
+ }
101
+ }
102
+ const NOT_A_WORKSPACE = "Not inside a Tessera workspace — no `courses/` directory was found at or above the current directory.";
103
+ function listHint(workspaceRoot) {
104
+ const courses = listCourses(workspaceRoot);
105
+ if (courses.length === 0) return "\nNo courses found. Create one with `tessera new <name>`.";
106
+ return `\nAvailable courses:\n${courses.map((c) => ` ${c}`).join("\n")}
107
+ Name one (\`tessera <command> <course>\`) or cd into its folder.`;
108
+ }
109
+ function resolveCourse(cwd, name) {
110
+ const here = resolve(cwd);
111
+ if (name) {
112
+ const nameError = validateProjectName(name, "the name");
113
+ if (nameError) return {
114
+ ok: false,
115
+ error: `Invalid course name "${name}" — ${nameError}.`
116
+ };
117
+ const workspaceRoot = findWorkspaceRoot(here);
118
+ if (!workspaceRoot) return {
119
+ ok: false,
120
+ error: NOT_A_WORKSPACE
121
+ };
122
+ const courseRoot = join(workspaceRoot, "courses", name);
123
+ if (!isCourse(courseRoot)) return {
124
+ ok: false,
125
+ error: `Course "${name}" not found in courses/.${listHint(workspaceRoot)}`
126
+ };
127
+ return {
128
+ ok: true,
129
+ courseRoot,
130
+ workspaceRoot
131
+ };
132
+ }
133
+ if (isCourse(here)) return {
134
+ ok: true,
135
+ courseRoot: here,
136
+ workspaceRoot: findWorkspaceRoot(here) ?? dirname(dirname(here))
137
+ };
138
+ const workspaceRoot = findWorkspaceRoot(here);
139
+ if (!workspaceRoot) return {
140
+ ok: false,
141
+ error: NOT_A_WORKSPACE
142
+ };
143
+ return {
144
+ ok: false,
145
+ error: `No course specified.${listHint(workspaceRoot)}`
146
+ };
147
+ }
148
+ //#endregion
149
+ //#region src/plugin/template-copy.ts
150
+ const RENAME = {
151
+ _gitignore: ".gitignore",
152
+ _gitkeep: ".gitkeep"
153
+ };
154
+ const TEXT = /\.(svelte|js|ts|json|css|md|html)$/;
155
+ function applyTokens(s, tokens) {
156
+ return s.replace(/__([A-Z_]+)__/g, (m, key) => key in tokens ? tokens[key] : m);
157
+ }
158
+ function copyTemplate(srcDir, destDir, tokens) {
159
+ mkdirSync(destDir, { recursive: true });
160
+ for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
161
+ const src = join(srcDir, entry.name);
162
+ const dest = join(destDir, RENAME[entry.name] ?? entry.name);
163
+ if (entry.isDirectory()) copyTemplate(src, dest, tokens);
164
+ else if (TEXT.test(entry.name)) writeFileSync(dest, applyTokens(readFileSync(src, "utf-8"), tokens));
165
+ else copyFileSync(src, dest);
166
+ }
167
+ }
168
+ //#endregion
169
+ //#region src/plugin/new-cli.ts
170
+ function runNew(name, cwd) {
171
+ if (name === "--help" || name === "-h") {
172
+ console.log("Usage: tessera new <name>\n\nScaffold a new course into courses/<name> inside the current workspace.");
173
+ return 0;
174
+ }
175
+ if (!name) {
176
+ console.error("Usage: tessera new <name>");
177
+ return 1;
178
+ }
179
+ const nameError = validateProjectName(name, "Course name");
180
+ if (nameError) {
181
+ console.error(`[tessera new] ${nameError}`);
182
+ return 1;
183
+ }
184
+ const workspaceRoot = findWorkspaceRoot(resolve(cwd));
185
+ if (!workspaceRoot) {
186
+ console.error("[tessera new] Not inside a Tessera workspace — run this from a workspace (a directory with a `courses/` folder).");
187
+ return 1;
188
+ }
189
+ const courseDir = join(workspaceRoot, "courses", name);
190
+ if (existsSync(courseDir)) {
191
+ console.error(`[tessera new] Course "${name}" already exists.`);
192
+ return 1;
193
+ }
194
+ copyTemplate(join(resolvePackageRoot(), "templates", "course"), courseDir, { PROJECT_TITLE: toTitleCase(name) });
195
+ const rel = relative(workspaceRoot, courseDir);
196
+ console.log(`\nCreated ${rel}.\n\nNext steps:\n pnpm tessera dev ${name}\n`);
197
+ return 0;
198
+ }
199
+ //#endregion
200
+ //#region src/plugin/duplicate-cli.ts
201
+ const HELP = "Usage: tessera duplicate <source> <new>\n\nCopy courses/<source>/ to courses/<new>/ within the current workspace.";
202
+ const SKIP = new Set([
203
+ "node_modules",
204
+ "dist",
205
+ "a11y-report.json",
206
+ ".vite"
207
+ ]);
208
+ function skip(srcPath) {
209
+ const name = basename(srcPath);
210
+ return SKIP.has(name) || name.startsWith(".tessera");
211
+ }
212
+ function runDuplicate(source, target, cwd) {
213
+ if (source === "--help" || source === "-h" || target === "--help" || target === "-h") {
214
+ console.log(HELP);
215
+ return 0;
216
+ }
217
+ if (!source || !target) {
218
+ console.error("Usage: tessera duplicate <source> <new>");
219
+ return 1;
220
+ }
221
+ const nameError = validateProjectName(target, "Course name");
222
+ if (nameError) {
223
+ console.error(`[tessera duplicate] ${nameError}`);
224
+ return 1;
225
+ }
226
+ const resolved = resolveCourse(cwd, source);
227
+ if (!resolved.ok) {
228
+ console.error(`[tessera duplicate] ${resolved.error}`);
229
+ return 1;
230
+ }
231
+ const { courseRoot: srcDir, workspaceRoot } = resolved;
232
+ const destDir = join(workspaceRoot, "courses", target);
233
+ if (existsSync(destDir)) {
234
+ console.error(`[tessera duplicate] Course "${target}" already exists.`);
235
+ return 1;
236
+ }
237
+ cpSync(srcDir, destDir, {
238
+ recursive: true,
239
+ filter: (src) => src === srcDir || !skip(src)
240
+ });
241
+ const rel = relative(workspaceRoot, destDir);
242
+ const srcRel = relative(workspaceRoot, srcDir);
243
+ console.log(`\nCreated ${rel} (duplicated from ${srcRel}).\n\nRemember to update the title in ${rel}/course.config.js.\n\nNext steps:\n pnpm tessera dev ${target}\n`);
244
+ return 0;
245
+ }
246
+ //#endregion
3
247
  //#region src/plugin/cli.ts
4
- const { errors, warnings } = validateProject(process.cwd());
5
- reportValidationIssues({
6
- errors,
7
- warnings
8
- });
9
- if (errors.length > 0) {
10
- const summary = `Validation failed with ${errors.length} error(s)` + (warnings.length > 0 ? ` and ${warnings.length} warning(s)` : "") + ".";
11
- console.error(`\n\x1b[31m${summary}\x1b[0m`);
12
- process.exit(1);
13
- }
14
- if (warnings.length > 0) console.log(`\n\x1b[33mValidation passed with ${warnings.length} warning(s).\x1b[0m`);
15
- else console.log("\x1B[32m[tessera]\x1B[0m Validation passed no issues found.");
16
- console.log("\x1B[2m[tessera] Static checks only. For a full runtime accessibility audit, run: npm run accessibility-check\x1B[0m");
17
- process.exit(0);
18
- //#endregion
19
- export {};
248
+ const USAGE = `Usage: tessera <command> [course] [options]
249
+
250
+ Commands:
251
+ new <name> Scaffold a new course into courses/<name>
252
+ duplicate <source> <new> Copy courses/<source> to courses/<new>
253
+ dev [course] Start the Vite dev server
254
+ export [course] Build and package the course for its LMS standard
255
+ validate [course] Fast static structure checks
256
+ a11y [course] Runtime accessibility audit (builds + drives Playwright)
257
+ check [course] Run validate, then a11y
258
+
259
+ Run a command from inside a course folder, or name the course explicitly.
260
+
261
+ a11y/check options:
262
+ --threshold <minor|moderate|serious|critical> Failing impact (default: serious)
263
+ --build Force a fresh build first`;
264
+ function splitCourseArg(rest) {
265
+ if (rest.length > 0 && !rest[0].startsWith("-")) return {
266
+ course: rest[0],
267
+ flags: rest.slice(1)
268
+ };
269
+ return {
270
+ course: void 0,
271
+ flags: rest
272
+ };
273
+ }
274
+ async function main(argv, cwd = process.cwd()) {
275
+ const [sub, ...rest] = argv;
276
+ if (sub === "new") return runNew(rest[0], cwd);
277
+ if (sub === "duplicate") return runDuplicate(rest[0], rest[1], cwd);
278
+ switch (sub) {
279
+ case "dev":
280
+ case "export":
281
+ case "validate":
282
+ case "a11y":
283
+ case "check": {
284
+ if (rest.includes("--help") || rest.includes("-h")) {
285
+ console.log(USAGE);
286
+ return 0;
287
+ }
288
+ const { course, flags } = splitCourseArg(rest);
289
+ const resolved = resolveCourse(cwd, course);
290
+ if (!resolved.ok) {
291
+ console.error(`[tessera] ${resolved.error}`);
292
+ return 1;
293
+ }
294
+ const { courseRoot, workspaceRoot } = resolved;
295
+ switch (sub) {
296
+ case "dev": {
297
+ const { runDev } = await import("../build-commands-C0OnV-Vg.js");
298
+ return runDev(courseRoot, workspaceRoot);
299
+ }
300
+ case "export": {
301
+ const { runBuild } = await import("../build-commands-C0OnV-Vg.js");
302
+ return runBuild(courseRoot, workspaceRoot);
303
+ }
304
+ case "validate": return runValidate(courseRoot);
305
+ case "check": {
306
+ const validateCode = runValidate(courseRoot);
307
+ if (validateCode !== 0) return validateCode;
308
+ return runA11y(courseRoot, workspaceRoot, flags);
309
+ }
310
+ case "a11y": return runA11y(courseRoot, workspaceRoot, flags);
311
+ }
312
+ return 0;
313
+ }
314
+ case "--help":
315
+ case "-h":
316
+ console.log(USAGE);
317
+ return 0;
318
+ case void 0:
319
+ console.error(`No command given.\n\n${USAGE}`);
320
+ return 1;
321
+ default:
322
+ console.error(`Unknown command: ${sub}\n\n${USAGE}`);
323
+ return 1;
324
+ }
325
+ }
326
+ if (import.meta.main) main(process.argv.slice(2)).then((code) => process.exit(code));
327
+ //#endregion
328
+ export { main, splitCourseArg };
20
329
 
21
330
  //# sourceMappingURL=cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../../src/plugin/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { validateProject, reportValidationIssues } from './validation.js';\n\nconst projectRoot = process.cwd();\nconst { errors, warnings } = validateProject(projectRoot);\n\nreportValidationIssues({ errors, warnings });\n\nif (errors.length > 0) {\n const summary =\n `Validation failed with ${errors.length} error(s)` +\n (warnings.length > 0 ? ` and ${warnings.length} warning(s)` : '') +\n '.';\n console.error(`\\n\\x1b[31m${summary}\\x1b[0m`);\n process.exit(1);\n}\n\nif (warnings.length > 0) {\n console.log(\n `\\n\\x1b[33mValidation passed with ${warnings.length} warning(s).\\x1b[0m`,\n );\n} else {\n console.log('\\x1b[32m[tessera]\\x1b[0m Validation passed — no issues found.');\n}\nconsole.log(\n '\\x1b[2m[tessera] Static checks only. For a full runtime accessibility audit, run: npm run accessibility-check\\x1b[0m',\n);\nprocess.exit(0);\n"],"mappings":";;;AAIA,MAAM,EAAE,QAAQ,aAAa,gBADT,QAAQ,KAC4B,CAAC;AAEzD,uBAAuB;CAAE;CAAQ;CAAU,CAAC;AAE5C,IAAI,OAAO,SAAS,GAAG;CACrB,MAAM,UACJ,0BAA0B,OAAO,OAAO,cACvC,SAAS,SAAS,IAAI,QAAQ,SAAS,OAAO,eAAe,MAC9D;CACF,QAAQ,MAAM,aAAa,QAAQ,SAAS;CAC5C,QAAQ,KAAK,EAAE;;AAGjB,IAAI,SAAS,SAAS,GACpB,QAAQ,IACN,oCAAoC,SAAS,OAAO,qBACrD;KAED,QAAQ,IAAI,gEAAgE;AAE9E,QAAQ,IACN,uHACD;AACD,QAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../../src/plugin/validate-cli.ts","../../src/plugin/a11y-cli.ts","../../src/plugin/project-name.ts","../../src/plugin/course-root.ts","../../src/plugin/template-copy.ts","../../src/plugin/new-cli.ts","../../src/plugin/duplicate-cli.ts","../../src/plugin/cli.ts"],"sourcesContent":["import { validateProject, reportValidationIssues } from './validation.js';\n\nexport function runValidate(projectRoot: string): number {\n const { errors, warnings } = validateProject(projectRoot);\n\n reportValidationIssues({ errors, warnings });\n\n if (errors.length > 0) {\n const summary =\n `Validation failed with ${errors.length} error(s)` +\n (warnings.length > 0 ? ` and ${warnings.length} warning(s)` : '') +\n '.';\n console.error(`\\n\\x1b[31m${summary}\\x1b[0m`);\n return 1;\n }\n\n if (warnings.length > 0) {\n console.log(\n `\\n\\x1b[33mValidation passed with ${warnings.length} warning(s).\\x1b[0m`,\n );\n } else {\n console.log(\n '\\x1b[32m[tessera]\\x1b[0m Validation passed — no issues found.',\n );\n }\n console.log(\n '\\x1b[2m[tessera] Static checks only. For a full runtime accessibility audit, run: pnpm exec tessera a11y\\x1b[0m',\n );\n return 0;\n}\n","import { runAudit, type AuditOptions, type ImpactLevel } from './a11y/audit.js';\n\nconst VALID_THRESHOLDS: ImpactLevel[] = [\n 'minor',\n 'moderate',\n 'serious',\n 'critical',\n];\n\nexport type ParsedA11yArgs =\n | { ok: true; args: AuditOptions }\n | { ok: false; error: string };\n\nexport function parseA11yArgs(argv: string[]): ParsedA11yArgs {\n let threshold: ImpactLevel | undefined;\n let rebuild = false;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === '--threshold') {\n const value = argv[++i] as ImpactLevel;\n if (!VALID_THRESHOLDS.includes(value)) {\n return {\n ok: false,\n error: `--threshold must be one of: ${VALID_THRESHOLDS.join(', ')}`,\n };\n }\n threshold = value;\n } else if (arg === '--build') {\n rebuild = true;\n } else {\n return { ok: false, error: `Unknown argument: ${arg}` };\n }\n }\n\n const args: AuditOptions = { rebuild };\n if (threshold !== undefined) args.threshold = threshold;\n return { ok: true, args };\n}\n\nexport async function runA11y(\n projectRoot: string,\n workspaceRoot: string,\n argv: string[],\n): Promise<number> {\n const parsed = parseA11yArgs(argv);\n if (!parsed.ok) {\n console.error(`[tessera a11y] ${parsed.error}`);\n return 1;\n }\n return runAudit(projectRoot, workspaceRoot, parsed.args);\n}\n","// Pure, dependency-free helpers shared by the `tessera new` subcommand and the\n// `create-tessera` scaffolder. Kept import-free so create-tessera can bundle it\n// at build time without dragging in the rest of the plugin (vite, svelte, …).\n\n// npm package name rules: 1-214 chars, lowercase, must start with [a-z0-9],\n// allowed chars [a-z0-9._-], no leading dot or underscore.\nexport function validateProjectName(\n name: string,\n label = 'Project name',\n): string | null {\n if (!name) return `${label} is required`;\n if (name.length > 214) return `${label} must be 214 characters or fewer`;\n if (name !== name.toLowerCase()) return `${label} must be lowercase`;\n if (!/^[a-z0-9]/.test(name)) {\n return `${label} must start with a letter or digit`;\n }\n if (!/^[a-z0-9._-]+$/.test(name)) {\n return `${label} may only contain lowercase letters, digits, \"-\", \"_\", and \".\"`;\n }\n return null;\n}\n\nexport function toTitleCase(slug: string): string {\n return slug\n .split(/[-_.\\s]+/)\n .filter(Boolean)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n}\n","import { existsSync, readdirSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { validateProjectName } from './project-name.js';\n\nexport interface ResolvedCourse {\n ok: true;\n courseRoot: string;\n workspaceRoot: string;\n}\n\nexport type ResolveResult = ResolvedCourse | { ok: false; error: string };\n\nfunction isDir(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction isCourse(dir: string): boolean {\n return existsSync(join(dir, 'course.config.js'));\n}\n\n// A workspace is the nearest ancestor holding a courses/ directory. The walk\n// includes the starting dir, so the workspace root resolves to itself.\nexport function findWorkspaceRoot(cwd: string): string | null {\n for (let dir = resolve(cwd); ; dir = dirname(dir)) {\n if (isDir(join(dir, 'courses'))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return null;\n }\n}\n\nexport function listCourses(workspaceRoot: string): string[] {\n const coursesDir = join(workspaceRoot, 'courses');\n try {\n return readdirSync(coursesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && isCourse(join(coursesDir, e.name)))\n .map((e) => e.name)\n .sort();\n } catch {\n return [];\n }\n}\n\nconst NOT_A_WORKSPACE =\n 'Not inside a Tessera workspace — no `courses/` directory was found at or above the current directory.';\n\nfunction listHint(workspaceRoot: string): string {\n const courses = listCourses(workspaceRoot);\n if (courses.length === 0) {\n return '\\nNo courses found. Create one with `tessera new <name>`.';\n }\n return (\n `\\nAvailable courses:\\n${courses.map((c) => ` ${c}`).join('\\n')}` +\n '\\nName one (`tessera <command> <course>`) or cd into its folder.'\n );\n}\n\n// A name argument always wins; otherwise the cwd must itself be a course. There\n// is deliberately no \"single course → use it implicitly\" rule, so a bare command\n// never changes meaning when a second course is added.\nexport function resolveCourse(cwd: string, name?: string): ResolveResult {\n const here = resolve(cwd);\n\n if (name) {\n const nameError = validateProjectName(name, 'the name');\n if (nameError) {\n return {\n ok: false,\n error: `Invalid course name \"${name}\" — ${nameError}.`,\n };\n }\n const workspaceRoot = findWorkspaceRoot(here);\n if (!workspaceRoot) return { ok: false, error: NOT_A_WORKSPACE };\n const courseRoot = join(workspaceRoot, 'courses', name);\n if (!isCourse(courseRoot)) {\n return {\n ok: false,\n error: `Course \"${name}\" not found in courses/.${listHint(workspaceRoot)}`,\n };\n }\n return { ok: true, courseRoot, workspaceRoot };\n }\n\n if (isCourse(here)) {\n const workspaceRoot = findWorkspaceRoot(here) ?? dirname(dirname(here));\n return { ok: true, courseRoot: here, workspaceRoot };\n }\n\n const workspaceRoot = findWorkspaceRoot(here);\n if (!workspaceRoot) return { ok: false, error: NOT_A_WORKSPACE };\n return {\n ok: false,\n error: `No course specified.${listHint(workspaceRoot)}`,\n };\n}\n","import {\n copyFileSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n writeFileSync,\n} from 'node:fs';\nimport { join } from 'node:path';\n\n// npm's tarball packing strips/renames leading-dot files, so templates store\n// them prefixed and we restore the dot on copy. (create-vite convention.)\nconst RENAME: Record<string, string> = {\n _gitignore: '.gitignore',\n _gitkeep: '.gitkeep',\n};\n\n// Text files get token substitution; everything else is copied byte-for-byte.\nconst TEXT = /\\.(svelte|js|ts|json|css|md|html)$/;\n\nfunction applyTokens(s: string, tokens: Record<string, string>): string {\n return s.replace(/__([A-Z_]+)__/g, (m, key) =>\n key in tokens ? tokens[key] : m,\n );\n}\n\nexport function copyTemplate(\n srcDir: string,\n destDir: string,\n tokens: Record<string, string>,\n): void {\n mkdirSync(destDir, { recursive: true });\n for (const entry of readdirSync(srcDir, { withFileTypes: true })) {\n const src = join(srcDir, entry.name);\n const dest = join(destDir, RENAME[entry.name] ?? entry.name);\n if (entry.isDirectory()) {\n copyTemplate(src, dest, tokens);\n } else if (TEXT.test(entry.name)) {\n writeFileSync(dest, applyTokens(readFileSync(src, 'utf-8'), tokens));\n } else {\n copyFileSync(src, dest);\n }\n }\n}\n","import { existsSync } from 'node:fs';\nimport { join, relative, resolve } from 'node:path';\nimport { findWorkspaceRoot } from './course-root.js';\nimport { validateProjectName, toTitleCase } from './project-name.js';\nimport { copyTemplate } from './template-copy.js';\nimport { resolvePackageRoot } from './package-root.js';\n\n// `tessera new <name>` — stamp a new course into courses/<name> inside the\n// current workspace. No install step: the workspace already owns the deps.\nexport function runNew(name: string | undefined, cwd: string): number {\n if (name === '--help' || name === '-h') {\n console.log(\n 'Usage: tessera new <name>\\n\\n' +\n 'Scaffold a new course into courses/<name> inside the current workspace.',\n );\n return 0;\n }\n if (!name) {\n console.error('Usage: tessera new <name>');\n return 1;\n }\n\n const nameError = validateProjectName(name, 'Course name');\n if (nameError) {\n console.error(`[tessera new] ${nameError}`);\n return 1;\n }\n\n const workspaceRoot = findWorkspaceRoot(resolve(cwd));\n if (!workspaceRoot) {\n console.error(\n '[tessera new] Not inside a Tessera workspace — run this from a workspace (a directory with a `courses/` folder).',\n );\n return 1;\n }\n\n const courseDir = join(workspaceRoot, 'courses', name);\n if (existsSync(courseDir)) {\n console.error(`[tessera new] Course \"${name}\" already exists.`);\n return 1;\n }\n\n const templateDir = join(resolvePackageRoot(), 'templates', 'course');\n copyTemplate(templateDir, courseDir, {\n PROJECT_TITLE: toTitleCase(name),\n });\n\n const rel = relative(workspaceRoot, courseDir);\n console.log(`\\nCreated ${rel}.\\n\\nNext steps:\\n pnpm tessera dev ${name}\\n`);\n return 0;\n}\n","import { cpSync, existsSync } from 'node:fs';\nimport { basename, join, relative } from 'node:path';\nimport { resolveCourse } from './course-root.js';\nimport { validateProjectName } from './project-name.js';\n\nconst HELP =\n 'Usage: tessera duplicate <source> <new>\\n\\n' +\n 'Copy courses/<source>/ to courses/<new>/ within the current workspace.';\n\n// Generated/build artifacts that should never travel with a verbatim copy. The\n// a11y throwaway build and Vite's cache live under node_modules, so they're\n// already pruned by the node_modules skip; the rest are belt-and-suspenders.\nconst SKIP = new Set(['node_modules', 'dist', 'a11y-report.json', '.vite']);\n\nfunction skip(srcPath: string): boolean {\n const name = basename(srcPath);\n return SKIP.has(name) || name.startsWith('.tessera');\n}\n\n// `tessera duplicate <source> <new>` — copy an existing course verbatim within\n// the current workspace. Unlike `new`, there is no template stamping: the JS\n// config (including its title) is copied untouched.\nexport function runDuplicate(\n source: string | undefined,\n target: string | undefined,\n cwd: string,\n): number {\n if (\n source === '--help' ||\n source === '-h' ||\n target === '--help' ||\n target === '-h'\n ) {\n console.log(HELP);\n return 0;\n }\n if (!source || !target) {\n console.error('Usage: tessera duplicate <source> <new>');\n return 1;\n }\n\n const nameError = validateProjectName(target, 'Course name');\n if (nameError) {\n console.error(`[tessera duplicate] ${nameError}`);\n return 1;\n }\n\n const resolved = resolveCourse(cwd, source);\n if (!resolved.ok) {\n console.error(`[tessera duplicate] ${resolved.error}`);\n return 1;\n }\n const { courseRoot: srcDir, workspaceRoot } = resolved;\n\n const destDir = join(workspaceRoot, 'courses', target);\n if (existsSync(destDir)) {\n console.error(`[tessera duplicate] Course \"${target}\" already exists.`);\n return 1;\n }\n\n cpSync(srcDir, destDir, {\n recursive: true,\n filter: (src) => src === srcDir || !skip(src),\n });\n\n const rel = relative(workspaceRoot, destDir);\n const srcRel = relative(workspaceRoot, srcDir);\n console.log(\n `\\nCreated ${rel} (duplicated from ${srcRel}).\\n\\n` +\n `Remember to update the title in ${rel}/course.config.js.\\n\\n` +\n `Next steps:\\n pnpm tessera dev ${target}\\n`,\n );\n return 0;\n}\n","#!/usr/bin/env node\nimport { runValidate } from './validate-cli.js';\nimport { runA11y } from './a11y-cli.js';\nimport { runNew } from './new-cli.js';\nimport { runDuplicate } from './duplicate-cli.js';\nimport { resolveCourse } from './course-root.js';\n\nconst USAGE = `Usage: tessera <command> [course] [options]\n\nCommands:\n new <name> Scaffold a new course into courses/<name>\n duplicate <source> <new> Copy courses/<source> to courses/<new>\n dev [course] Start the Vite dev server\n export [course] Build and package the course for its LMS standard\n validate [course] Fast static structure checks\n a11y [course] Runtime accessibility audit (builds + drives Playwright)\n check [course] Run validate, then a11y\n\nRun a command from inside a course folder, or name the course explicitly.\n\na11y/check options:\n --threshold <minor|moderate|serious|critical> Failing impact (default: serious)\n --build Force a fresh build first`;\n\n// The course is a leading positional: `tessera <cmd> [course] [flags]`. Only the\n// first token can be the course, and only when it isn't a flag — otherwise a flag\n// value (e.g. the `serious` in `--threshold serious`) would be misread as a name.\nexport function splitCourseArg(rest: string[]): {\n course?: string;\n flags: string[];\n} {\n if (rest.length > 0 && !rest[0].startsWith('-')) {\n return { course: rest[0], flags: rest.slice(1) };\n }\n return { course: undefined, flags: rest };\n}\n\nexport async function main(\n argv: string[],\n cwd: string = process.cwd(),\n): Promise<number> {\n const [sub, ...rest] = argv;\n\n if (sub === 'new') return runNew(rest[0], cwd);\n if (sub === 'duplicate') return runDuplicate(rest[0], rest[1], cwd);\n\n switch (sub) {\n case 'dev':\n case 'export':\n case 'validate':\n case 'a11y':\n case 'check': {\n if (rest.includes('--help') || rest.includes('-h')) {\n console.log(USAGE);\n return 0;\n }\n const { course, flags } = splitCourseArg(rest);\n const resolved = resolveCourse(cwd, course);\n if (!resolved.ok) {\n console.error(`[tessera] ${resolved.error}`);\n return 1;\n }\n const { courseRoot, workspaceRoot } = resolved;\n\n switch (sub) {\n case 'dev': {\n const { runDev } = await import('./build-commands.js');\n return runDev(courseRoot, workspaceRoot);\n }\n case 'export': {\n const { runBuild } = await import('./build-commands.js');\n return runBuild(courseRoot, workspaceRoot);\n }\n case 'validate':\n return runValidate(courseRoot);\n case 'check': {\n const validateCode = runValidate(courseRoot);\n if (validateCode !== 0) return validateCode;\n return runA11y(courseRoot, workspaceRoot, flags);\n }\n case 'a11y':\n return runA11y(courseRoot, workspaceRoot, flags);\n }\n return 0;\n }\n case '--help':\n case '-h':\n console.log(USAGE);\n return 0;\n case undefined:\n console.error(`No command given.\\n\\n${USAGE}`);\n return 1;\n default:\n console.error(`Unknown command: ${sub}\\n\\n${USAGE}`);\n return 1;\n }\n}\n\n// import.meta.main is true only when this module is the program entry point,\n// and resolves symlinks itself (pnpm/npm bin shims) — Node >= 24.\nif (import.meta.main) {\n void main(process.argv.slice(2)).then((code) => process.exit(code));\n}\n"],"mappings":"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"}
@@ -13,7 +13,7 @@ type ImpactLevel = 'minor' | 'moderate' | 'serious' | 'critical';
13
13
  * reuses) dist/, serves it, drives Playwright + axe-core over each page, writes
14
14
  * a11y-report.json, and returns a process exit code (0 pass, 1 fail/error).
15
15
  */
16
- declare function runAudit(projectRoot: string, options?: AuditOptions): Promise<number>;
16
+ declare function runAudit(projectRoot: string, workspaceRoot: string, options?: AuditOptions): Promise<number>;
17
17
  //#endregion
18
18
  //#region src/plugin/index.d.ts
19
19
  declare function tesseraPlugin(): (Plugin<any> | Plugin<any>[])[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/plugin/a11y/audit.ts","../../src/plugin/index.ts"],"mappings":";;;UAKiB,YAAA;;EAEf,SAAA,GAAY,WAAA;EAFe;EAI3B,OAAA;AAAA;AAAA,KAGU,WAAA;;;;;AA6GZ;iBAAsB,QAAA,CACpB,WAAA,UACA,OAAA,GAAS,YAAA,GACR,OAAA;;;iBCtCa,aAAA,CAAA,IAAa,MAAA,QAAA,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/plugin/a11y/audit.ts","../../src/plugin/index.ts"],"mappings":";;;UAKiB,YAAA;;EAEf,SAAA,GAAY,WAAW;EAFI;EAI3B,OAAA;AAAA;AAAA,KAGU,WAAA;;;AAAW;AAiHvB;;iBAAsB,QAAA,CACpB,WAAA,UACA,aAAA,UACA,OAAA,GAAS,YAAA,GACR,OAAO;;;iBCbM,aAAA,KAAa,MAAA,QAAA,MAAA"}