@lxpack/validators 0.1.1 → 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.
- package/README.md +35 -13
- package/dist/index.d.ts +240 -29
- package/dist/index.js +655 -292
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/eddiethedean/lxpack/blob/main/LICENSE)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
|
|
8
|
-
Zod schemas and filesystem validation for LXPack course manifests.
|
|
8
|
+
Zod schemas and filesystem validation for LXPack course manifests — including flow, variables, and component lessons (v0.2.0).
|
|
9
9
|
|
|
10
10
|
Part of [LXPack](https://github.com/eddiethedean/lxpack) — an AI-native learning experience compiler and runtime.
|
|
11
11
|
|
|
@@ -14,6 +14,7 @@ Part of [LXPack](https://github.com/eddiethedean/lxpack) — an AI-native learni
|
|
|
14
14
|
| CLI | [`@lxpack/cli`](../cli/README.md) |
|
|
15
15
|
| Packaging | [`@lxpack/scorm`](../scorm/README.md) |
|
|
16
16
|
| Runtime | [`@lxpack/runtime`](../runtime/README.md) |
|
|
17
|
+
| Components | [`@lxpack/components`](../components/README.md) |
|
|
17
18
|
|
|
18
19
|
## Install
|
|
19
20
|
|
|
@@ -44,7 +45,9 @@ if (!result.valid) {
|
|
|
44
45
|
const { manifest } = result;
|
|
45
46
|
const bundle = await buildRuntimeAssessmentBundle("/path/to/my-course", manifest);
|
|
46
47
|
// bundle.assessments — learner-facing questions (no correct flags)
|
|
47
|
-
// bundle.answerKeys — scoring keys for the runtime
|
|
48
|
+
// bundle.answerKeys — scoring keys for the runtime
|
|
49
|
+
// bundle.configs — maxAttempts, shuffleChoices, showFeedback per assessment
|
|
50
|
+
// bundle.feedback — questionId → explanation text for feedback modes
|
|
48
51
|
}
|
|
49
52
|
```
|
|
50
53
|
|
|
@@ -65,6 +68,14 @@ if (Array.isArray(loaded)) {
|
|
|
65
68
|
const parsed = courseManifestSchema.safeParse(manifestObject);
|
|
66
69
|
```
|
|
67
70
|
|
|
71
|
+
### Flow validation
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { validateFlow, detectFlowCycles, collectActivityIds } from "@lxpack/validators";
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`validateCourse` runs flow checks when `manifest.flow` is present: valid `goto` targets, known condition shapes, and cycle detection.
|
|
78
|
+
|
|
68
79
|
### Safe path resolution
|
|
69
80
|
|
|
70
81
|
```ts
|
|
@@ -78,34 +89,43 @@ isPathContained(courseDir, abs); // true if inside course root
|
|
|
78
89
|
|
|
79
90
|
| Export | Description |
|
|
80
91
|
|--------|-------------|
|
|
81
|
-
| `validateCourse(dir)` | Parse `course.yaml`, validate schema,
|
|
92
|
+
| `validateCourse(dir)` | Parse `course.yaml`, validate schema, flow, files, symlink containment |
|
|
82
93
|
| `loadManifest(courseDir)` | Load and parse `course.yaml` |
|
|
83
|
-
| `buildRuntimeAssessmentBundle(dir, manifest)` | Load assessments; split learner view
|
|
84
|
-
| `toLearnerAssessment(assessment)` | Strip `correct`
|
|
94
|
+
| `buildRuntimeAssessmentBundle(dir, manifest)` | Load assessments; split learner view, keys, configs, feedback |
|
|
95
|
+
| `toLearnerAssessment(assessment)` | Strip `correct` from choices; extract config and feedback maps |
|
|
96
|
+
| `validateFlow(manifest)` | Flow rule and target validation |
|
|
97
|
+
| `detectFlowCycles(flow)` | Cycle detection for branching graphs |
|
|
98
|
+
| `collectActivityIds(manifest)` | Lesson and assessment IDs for flow targets |
|
|
99
|
+
| `conditionSchema`, `flowRuleSchema` | Zod schemas for flow conditions and rules |
|
|
100
|
+
| `BUILTIN_COMPONENT_IDS`, `isBuiltinComponentId` | Allowed built-in component lesson IDs |
|
|
85
101
|
| `resolveCoursePath(dir, relativePath)` | Resolve a path safely inside the course directory |
|
|
86
102
|
| `isPathContained(root, target)` | Whether `target` stays under `root` |
|
|
87
103
|
| `courseManifestSchema` | Zod schema for the full course manifest |
|
|
88
|
-
| `lessonSchema`, `assessmentSchema`, … | Strict sub-schemas
|
|
89
|
-
| `CourseManifest`, `Lesson`, `Assessment`, `
|
|
104
|
+
| `lessonSchema`, `assessmentSchema`, `variableDefSchema`, … | Strict sub-schemas |
|
|
105
|
+
| `CourseManifest`, `Lesson`, `Assessment`, `FlowRule`, `VariableDef`, `RuntimeAssessmentBundle` | TypeScript types |
|
|
90
106
|
|
|
91
107
|
## What gets validated
|
|
92
108
|
|
|
93
|
-
- Manifest shape
|
|
94
|
-
- Lesson types: `markdown` (`file`)
|
|
95
|
-
-
|
|
109
|
+
- Manifest shape: lessons, assessments, optional `variables` and `flow`, tracking rules
|
|
110
|
+
- Lesson types: `markdown` (`file`), `html` (`path`), `component` (`component` + optional `props`)
|
|
111
|
+
- Component IDs: built-in IDs or course overrides under `components/<id>/`
|
|
112
|
+
- Flow rules: condition grammar, `goto` targets that reference known activity IDs, acyclic flow (warnings for cycles)
|
|
113
|
+
- Assessment YAML: strict MCQ schemas; optional `maxAttempts`, `shuffleChoices`, `showFeedback`; `explanation` per question
|
|
96
114
|
- Duplicate lesson IDs
|
|
97
115
|
- Path containment — referenced files must stay inside the course directory (including via symlinks)
|
|
98
|
-
- On-disk assets: files exist and
|
|
116
|
+
- On-disk assets: files exist and assessment paths are regular files
|
|
99
117
|
|
|
100
118
|
## Assessment packaging
|
|
101
119
|
|
|
102
120
|
Author assessments live as YAML under `assessments/` in the course repo. At build/preview time:
|
|
103
121
|
|
|
104
122
|
1. `buildRuntimeAssessmentBundle()` reads each assessment file.
|
|
105
|
-
2. **Learner payload** — questions and choices without `correct`
|
|
123
|
+
2. **Learner payload** — questions and choices without `correct` flags.
|
|
106
124
|
3. **Answer keys** — `questionId → choiceId` map for scoring.
|
|
125
|
+
4. **Configs** — per-assessment quiz behavior (`maxAttempts`, `shuffleChoices`, `showFeedback`).
|
|
126
|
+
5. **Feedback** — `questionId → explanation` for immediate/end feedback (not shipped as separate files).
|
|
107
127
|
|
|
108
|
-
The CLI and [`@lxpack/scorm`](../scorm/README.md) embed
|
|
128
|
+
The CLI and [`@lxpack/scorm`](../scorm/README.md) embed all of this in the HTML config JSON. Exported ZIPs **do not** include `assessments/` files, so answer keys are not fetchable as static assets.
|
|
109
129
|
|
|
110
130
|
## Development
|
|
111
131
|
|
|
@@ -120,6 +140,8 @@ pnpm --filter @lxpack/validators typecheck
|
|
|
120
140
|
## Links
|
|
121
141
|
|
|
122
142
|
- [LXPack repository](https://github.com/eddiethedean/lxpack)
|
|
143
|
+
- [Documentation index](https://github.com/eddiethedean/lxpack/blob/main/docs/README.md)
|
|
144
|
+
- [Technical specification](https://github.com/eddiethedean/lxpack/blob/main/docs/SPEC.md)
|
|
123
145
|
- [Changelog](https://github.com/eddiethedean/lxpack/blob/main/CHANGELOG.md)
|
|
124
146
|
|
|
125
147
|
## License
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
type Condition = {
|
|
4
|
+
variable: {
|
|
5
|
+
eq: [string, string | number | boolean];
|
|
6
|
+
};
|
|
7
|
+
} | {
|
|
8
|
+
assessment: {
|
|
9
|
+
passed: string;
|
|
10
|
+
};
|
|
11
|
+
} | {
|
|
12
|
+
interaction: {
|
|
13
|
+
done: string;
|
|
14
|
+
};
|
|
15
|
+
} | {
|
|
16
|
+
all: Condition[];
|
|
17
|
+
} | {
|
|
18
|
+
any: Condition[];
|
|
19
|
+
};
|
|
20
|
+
declare const conditionSchema: z.ZodType<Condition>;
|
|
21
|
+
declare const flowRuleSchema: z.ZodObject<{
|
|
22
|
+
when: z.ZodType<Condition, z.ZodTypeDef, Condition>;
|
|
23
|
+
goto: z.ZodString;
|
|
24
|
+
}, "strict", z.ZodTypeAny, {
|
|
25
|
+
when: Condition;
|
|
26
|
+
goto: string;
|
|
27
|
+
}, {
|
|
28
|
+
when: Condition;
|
|
29
|
+
goto: string;
|
|
30
|
+
}>;
|
|
31
|
+
type FlowRule = z.infer<typeof flowRuleSchema>;
|
|
32
|
+
|
|
3
33
|
declare const assessmentQuestionSchema: z.ZodEffects<z.ZodObject<{
|
|
4
34
|
id: z.ZodString;
|
|
5
35
|
prompt: z.ZodString;
|
|
@@ -60,13 +90,13 @@ declare const markdownLessonSchema: z.ZodObject<{
|
|
|
60
90
|
file: z.ZodString;
|
|
61
91
|
title: z.ZodOptional<z.ZodString>;
|
|
62
92
|
}, "strict", z.ZodTypeAny, {
|
|
63
|
-
id: string;
|
|
64
93
|
type: "markdown";
|
|
94
|
+
id: string;
|
|
65
95
|
file: string;
|
|
66
96
|
title?: string | undefined;
|
|
67
97
|
}, {
|
|
68
|
-
id: string;
|
|
69
98
|
type: "markdown";
|
|
99
|
+
id: string;
|
|
70
100
|
file: string;
|
|
71
101
|
title?: string | undefined;
|
|
72
102
|
}>;
|
|
@@ -76,15 +106,34 @@ declare const htmlLessonSchema: z.ZodObject<{
|
|
|
76
106
|
path: z.ZodString;
|
|
77
107
|
title: z.ZodOptional<z.ZodString>;
|
|
78
108
|
}, "strict", z.ZodTypeAny, {
|
|
79
|
-
id: string;
|
|
80
109
|
path: string;
|
|
81
110
|
type: "html";
|
|
111
|
+
id: string;
|
|
82
112
|
title?: string | undefined;
|
|
83
113
|
}, {
|
|
84
|
-
id: string;
|
|
85
114
|
path: string;
|
|
86
115
|
type: "html";
|
|
116
|
+
id: string;
|
|
117
|
+
title?: string | undefined;
|
|
118
|
+
}>;
|
|
119
|
+
declare const componentLessonSchema: z.ZodObject<{
|
|
120
|
+
id: z.ZodString;
|
|
121
|
+
type: z.ZodLiteral<"component">;
|
|
122
|
+
component: z.ZodString;
|
|
123
|
+
props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
124
|
+
title: z.ZodOptional<z.ZodString>;
|
|
125
|
+
}, "strict", z.ZodTypeAny, {
|
|
126
|
+
type: "component";
|
|
127
|
+
id: string;
|
|
128
|
+
component: string;
|
|
129
|
+
title?: string | undefined;
|
|
130
|
+
props?: Record<string, unknown> | undefined;
|
|
131
|
+
}, {
|
|
132
|
+
type: "component";
|
|
133
|
+
id: string;
|
|
134
|
+
component: string;
|
|
87
135
|
title?: string | undefined;
|
|
136
|
+
props?: Record<string, unknown> | undefined;
|
|
88
137
|
}>;
|
|
89
138
|
declare const lessonSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
90
139
|
id: z.ZodString;
|
|
@@ -92,13 +141,13 @@ declare const lessonSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
|
92
141
|
file: z.ZodString;
|
|
93
142
|
title: z.ZodOptional<z.ZodString>;
|
|
94
143
|
}, "strict", z.ZodTypeAny, {
|
|
95
|
-
id: string;
|
|
96
144
|
type: "markdown";
|
|
145
|
+
id: string;
|
|
97
146
|
file: string;
|
|
98
147
|
title?: string | undefined;
|
|
99
148
|
}, {
|
|
100
|
-
id: string;
|
|
101
149
|
type: "markdown";
|
|
150
|
+
id: string;
|
|
102
151
|
file: string;
|
|
103
152
|
title?: string | undefined;
|
|
104
153
|
}>, z.ZodObject<{
|
|
@@ -107,20 +156,42 @@ declare const lessonSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
|
107
156
|
path: z.ZodString;
|
|
108
157
|
title: z.ZodOptional<z.ZodString>;
|
|
109
158
|
}, "strict", z.ZodTypeAny, {
|
|
110
|
-
id: string;
|
|
111
159
|
path: string;
|
|
112
160
|
type: "html";
|
|
161
|
+
id: string;
|
|
113
162
|
title?: string | undefined;
|
|
114
163
|
}, {
|
|
115
|
-
id: string;
|
|
116
164
|
path: string;
|
|
117
165
|
type: "html";
|
|
166
|
+
id: string;
|
|
167
|
+
title?: string | undefined;
|
|
168
|
+
}>, z.ZodObject<{
|
|
169
|
+
id: z.ZodString;
|
|
170
|
+
type: z.ZodLiteral<"component">;
|
|
171
|
+
component: z.ZodString;
|
|
172
|
+
props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
173
|
+
title: z.ZodOptional<z.ZodString>;
|
|
174
|
+
}, "strict", z.ZodTypeAny, {
|
|
175
|
+
type: "component";
|
|
176
|
+
id: string;
|
|
177
|
+
component: string;
|
|
118
178
|
title?: string | undefined;
|
|
179
|
+
props?: Record<string, unknown> | undefined;
|
|
180
|
+
}, {
|
|
181
|
+
type: "component";
|
|
182
|
+
id: string;
|
|
183
|
+
component: string;
|
|
184
|
+
title?: string | undefined;
|
|
185
|
+
props?: Record<string, unknown> | undefined;
|
|
119
186
|
}>]>;
|
|
187
|
+
declare const showFeedbackSchema: z.ZodDefault<z.ZodEnum<["immediate", "end", "never"]>>;
|
|
120
188
|
declare const assessmentSchema: z.ZodObject<{
|
|
121
189
|
id: z.ZodString;
|
|
122
190
|
title: z.ZodOptional<z.ZodString>;
|
|
123
191
|
passingScore: z.ZodDefault<z.ZodNumber>;
|
|
192
|
+
maxAttempts: z.ZodOptional<z.ZodNumber>;
|
|
193
|
+
shuffleChoices: z.ZodOptional<z.ZodBoolean>;
|
|
194
|
+
showFeedback: z.ZodOptional<z.ZodDefault<z.ZodEnum<["immediate", "end", "never"]>>>;
|
|
124
195
|
questions: z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
125
196
|
id: z.ZodString;
|
|
126
197
|
prompt: z.ZodString;
|
|
@@ -189,6 +260,9 @@ declare const assessmentSchema: z.ZodObject<{
|
|
|
189
260
|
explanation?: string | undefined;
|
|
190
261
|
}[];
|
|
191
262
|
title?: string | undefined;
|
|
263
|
+
maxAttempts?: number | undefined;
|
|
264
|
+
shuffleChoices?: boolean | undefined;
|
|
265
|
+
showFeedback?: "never" | "immediate" | "end" | undefined;
|
|
192
266
|
}, {
|
|
193
267
|
id: string;
|
|
194
268
|
questions: {
|
|
@@ -203,6 +277,9 @@ declare const assessmentSchema: z.ZodObject<{
|
|
|
203
277
|
}[];
|
|
204
278
|
title?: string | undefined;
|
|
205
279
|
passingScore?: number | undefined;
|
|
280
|
+
maxAttempts?: number | undefined;
|
|
281
|
+
shuffleChoices?: boolean | undefined;
|
|
282
|
+
showFeedback?: "never" | "immediate" | "end" | undefined;
|
|
206
283
|
}>;
|
|
207
284
|
declare const assessmentRefSchema: z.ZodObject<{
|
|
208
285
|
id: z.ZodString;
|
|
@@ -214,6 +291,22 @@ declare const assessmentRefSchema: z.ZodObject<{
|
|
|
214
291
|
id: string;
|
|
215
292
|
file: string;
|
|
216
293
|
}>;
|
|
294
|
+
declare const variableDefSchema: z.ZodEffects<z.ZodObject<{
|
|
295
|
+
default: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
296
|
+
type: z.ZodOptional<z.ZodEnum<["string", "number", "boolean"]>>;
|
|
297
|
+
}, "strict", z.ZodTypeAny, {
|
|
298
|
+
default: string | number | boolean;
|
|
299
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
300
|
+
}, {
|
|
301
|
+
default: string | number | boolean;
|
|
302
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
303
|
+
}>, {
|
|
304
|
+
default: string | number | boolean;
|
|
305
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
306
|
+
}, {
|
|
307
|
+
default: string | number | boolean;
|
|
308
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
309
|
+
}>;
|
|
217
310
|
declare const trackingSchema: z.ZodOptional<z.ZodObject<{
|
|
218
311
|
completion: z.ZodOptional<z.ZodObject<{
|
|
219
312
|
threshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -238,6 +331,7 @@ declare const runtimeConfigSchema: z.ZodOptional<z.ZodObject<{
|
|
|
238
331
|
}, {
|
|
239
332
|
theme?: string | undefined;
|
|
240
333
|
}>>;
|
|
334
|
+
|
|
241
335
|
declare const courseManifestSchema: z.ZodObject<{
|
|
242
336
|
title: z.ZodString;
|
|
243
337
|
version: z.ZodString;
|
|
@@ -266,19 +360,45 @@ declare const courseManifestSchema: z.ZodObject<{
|
|
|
266
360
|
threshold?: number | undefined;
|
|
267
361
|
} | undefined;
|
|
268
362
|
}>>;
|
|
363
|
+
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
|
|
364
|
+
default: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
365
|
+
type: z.ZodOptional<z.ZodEnum<["string", "number", "boolean"]>>;
|
|
366
|
+
}, "strict", z.ZodTypeAny, {
|
|
367
|
+
default: string | number | boolean;
|
|
368
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
369
|
+
}, {
|
|
370
|
+
default: string | number | boolean;
|
|
371
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
372
|
+
}>, {
|
|
373
|
+
default: string | number | boolean;
|
|
374
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
375
|
+
}, {
|
|
376
|
+
default: string | number | boolean;
|
|
377
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
378
|
+
}>>>;
|
|
379
|
+
flow: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
380
|
+
when: z.ZodType<Condition, z.ZodTypeDef, Condition>;
|
|
381
|
+
goto: z.ZodString;
|
|
382
|
+
}, "strict", z.ZodTypeAny, {
|
|
383
|
+
when: Condition;
|
|
384
|
+
goto: string;
|
|
385
|
+
}, {
|
|
386
|
+
when: Condition;
|
|
387
|
+
goto: string;
|
|
388
|
+
}>, "many">>;
|
|
269
389
|
lessons: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
270
390
|
id: z.ZodString;
|
|
271
391
|
type: z.ZodLiteral<"markdown">;
|
|
272
392
|
file: z.ZodString;
|
|
273
393
|
title: z.ZodOptional<z.ZodString>;
|
|
274
394
|
}, "strict", z.ZodTypeAny, {
|
|
275
|
-
id: string;
|
|
276
395
|
type: "markdown";
|
|
396
|
+
id: string;
|
|
277
397
|
file: string;
|
|
278
398
|
title?: string | undefined;
|
|
279
399
|
}, {
|
|
280
|
-
id: string;
|
|
281
400
|
type: "markdown";
|
|
401
|
+
id: string;
|
|
282
402
|
file: string;
|
|
283
403
|
title?: string | undefined;
|
|
284
404
|
}>, z.ZodObject<{
|
|
@@ -287,15 +407,33 @@ declare const courseManifestSchema: z.ZodObject<{
|
|
|
287
407
|
path: z.ZodString;
|
|
288
408
|
title: z.ZodOptional<z.ZodString>;
|
|
289
409
|
}, "strict", z.ZodTypeAny, {
|
|
290
|
-
id: string;
|
|
291
410
|
path: string;
|
|
292
411
|
type: "html";
|
|
412
|
+
id: string;
|
|
293
413
|
title?: string | undefined;
|
|
294
414
|
}, {
|
|
295
|
-
id: string;
|
|
296
415
|
path: string;
|
|
297
416
|
type: "html";
|
|
417
|
+
id: string;
|
|
418
|
+
title?: string | undefined;
|
|
419
|
+
}>, z.ZodObject<{
|
|
420
|
+
id: z.ZodString;
|
|
421
|
+
type: z.ZodLiteral<"component">;
|
|
422
|
+
component: z.ZodString;
|
|
423
|
+
props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
424
|
+
title: z.ZodOptional<z.ZodString>;
|
|
425
|
+
}, "strict", z.ZodTypeAny, {
|
|
426
|
+
type: "component";
|
|
427
|
+
id: string;
|
|
428
|
+
component: string;
|
|
429
|
+
title?: string | undefined;
|
|
430
|
+
props?: Record<string, unknown> | undefined;
|
|
431
|
+
}, {
|
|
432
|
+
type: "component";
|
|
433
|
+
id: string;
|
|
434
|
+
component: string;
|
|
298
435
|
title?: string | undefined;
|
|
436
|
+
props?: Record<string, unknown> | undefined;
|
|
299
437
|
}>]>, "many">;
|
|
300
438
|
assessments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
301
439
|
id: z.ZodString;
|
|
@@ -311,15 +449,21 @@ declare const courseManifestSchema: z.ZodObject<{
|
|
|
311
449
|
title: string;
|
|
312
450
|
version: string;
|
|
313
451
|
lessons: ({
|
|
314
|
-
id: string;
|
|
315
452
|
type: "markdown";
|
|
453
|
+
id: string;
|
|
316
454
|
file: string;
|
|
317
455
|
title?: string | undefined;
|
|
318
456
|
} | {
|
|
319
|
-
id: string;
|
|
320
457
|
path: string;
|
|
321
458
|
type: "html";
|
|
459
|
+
id: string;
|
|
460
|
+
title?: string | undefined;
|
|
461
|
+
} | {
|
|
462
|
+
type: "component";
|
|
463
|
+
id: string;
|
|
464
|
+
component: string;
|
|
322
465
|
title?: string | undefined;
|
|
466
|
+
props?: Record<string, unknown> | undefined;
|
|
323
467
|
})[];
|
|
324
468
|
description?: string | undefined;
|
|
325
469
|
runtime?: {
|
|
@@ -330,6 +474,14 @@ declare const courseManifestSchema: z.ZodObject<{
|
|
|
330
474
|
threshold: number;
|
|
331
475
|
} | undefined;
|
|
332
476
|
} | undefined;
|
|
477
|
+
variables?: Record<string, {
|
|
478
|
+
default: string | number | boolean;
|
|
479
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
480
|
+
}> | undefined;
|
|
481
|
+
flow?: {
|
|
482
|
+
when: Condition;
|
|
483
|
+
goto: string;
|
|
484
|
+
}[] | undefined;
|
|
333
485
|
assessments?: {
|
|
334
486
|
id: string;
|
|
335
487
|
file: string;
|
|
@@ -338,15 +490,21 @@ declare const courseManifestSchema: z.ZodObject<{
|
|
|
338
490
|
title: string;
|
|
339
491
|
version: string;
|
|
340
492
|
lessons: ({
|
|
341
|
-
id: string;
|
|
342
493
|
type: "markdown";
|
|
494
|
+
id: string;
|
|
343
495
|
file: string;
|
|
344
496
|
title?: string | undefined;
|
|
345
497
|
} | {
|
|
346
|
-
id: string;
|
|
347
498
|
path: string;
|
|
348
499
|
type: "html";
|
|
500
|
+
id: string;
|
|
501
|
+
title?: string | undefined;
|
|
502
|
+
} | {
|
|
503
|
+
type: "component";
|
|
504
|
+
id: string;
|
|
505
|
+
component: string;
|
|
349
506
|
title?: string | undefined;
|
|
507
|
+
props?: Record<string, unknown> | undefined;
|
|
350
508
|
})[];
|
|
351
509
|
description?: string | undefined;
|
|
352
510
|
runtime?: {
|
|
@@ -357,6 +515,14 @@ declare const courseManifestSchema: z.ZodObject<{
|
|
|
357
515
|
threshold?: number | undefined;
|
|
358
516
|
} | undefined;
|
|
359
517
|
} | undefined;
|
|
518
|
+
variables?: Record<string, {
|
|
519
|
+
default: string | number | boolean;
|
|
520
|
+
type?: "string" | "number" | "boolean" | undefined;
|
|
521
|
+
}> | undefined;
|
|
522
|
+
flow?: {
|
|
523
|
+
when: Condition;
|
|
524
|
+
goto: string;
|
|
525
|
+
}[] | undefined;
|
|
360
526
|
assessments?: {
|
|
361
527
|
id: string;
|
|
362
528
|
file: string;
|
|
@@ -366,19 +532,15 @@ type CourseManifest = z.infer<typeof courseManifestSchema>;
|
|
|
366
532
|
type Lesson = z.infer<typeof lessonSchema>;
|
|
367
533
|
type Assessment = z.infer<typeof assessmentSchema>;
|
|
368
534
|
type AssessmentRef = z.infer<typeof assessmentRefSchema>;
|
|
535
|
+
type VariableDef = z.infer<typeof variableDefSchema>;
|
|
536
|
+
type ComponentLesson = z.infer<typeof componentLessonSchema>;
|
|
537
|
+
type ShowFeedback = z.infer<typeof showFeedbackSchema>;
|
|
538
|
+
|
|
539
|
+
/** Built-in component ids shipped in @lxpack/components */
|
|
540
|
+
declare const BUILTIN_COMPONENT_IDS: readonly ["callout", "image-card", "checklist"];
|
|
541
|
+
type BuiltinComponentId = (typeof BUILTIN_COMPONENT_IDS)[number];
|
|
542
|
+
declare function isBuiltinComponentId(id: string): id is BuiltinComponentId;
|
|
369
543
|
|
|
370
|
-
declare function formatErrorMessage(err: unknown): string;
|
|
371
|
-
declare function formatIssuePath(path: PropertyKey[]): string;
|
|
372
|
-
interface ValidationIssue {
|
|
373
|
-
path: string;
|
|
374
|
-
message: string;
|
|
375
|
-
severity: "error" | "warning";
|
|
376
|
-
}
|
|
377
|
-
interface ValidationResult {
|
|
378
|
-
valid: boolean;
|
|
379
|
-
manifest?: CourseManifest;
|
|
380
|
-
issues: ValidationIssue[];
|
|
381
|
-
}
|
|
382
544
|
declare function isPathContained(rootDir: string, candidatePath: string): boolean;
|
|
383
545
|
declare function resolveCoursePath(courseDir: string, relativePath: string): {
|
|
384
546
|
ok: true;
|
|
@@ -393,12 +555,33 @@ declare function assertResolvedPathContained(courseDir: string, resolvedPath: st
|
|
|
393
555
|
ok: false;
|
|
394
556
|
message: string;
|
|
395
557
|
};
|
|
558
|
+
|
|
559
|
+
declare function formatErrorMessage(err: unknown): string;
|
|
560
|
+
declare function formatIssuePath(path: PropertyKey[]): string;
|
|
561
|
+
interface ValidationIssue {
|
|
562
|
+
path: string;
|
|
563
|
+
message: string;
|
|
564
|
+
severity: "error" | "warning";
|
|
565
|
+
}
|
|
566
|
+
interface ValidationResult {
|
|
567
|
+
valid: boolean;
|
|
568
|
+
manifest?: CourseManifest;
|
|
569
|
+
issues: ValidationIssue[];
|
|
570
|
+
/** Populated when assessment files parse successfully. */
|
|
571
|
+
parsedAssessments?: Map<string, Assessment>;
|
|
572
|
+
}
|
|
396
573
|
declare function loadManifest(courseDir: string): Promise<{
|
|
397
574
|
manifest: CourseManifest;
|
|
398
575
|
raw: unknown;
|
|
399
576
|
} | ValidationIssue[]>;
|
|
400
577
|
declare function validateCourse(courseDir: string): Promise<ValidationResult>;
|
|
401
578
|
|
|
579
|
+
declare function collectActivityIds(manifest: CourseManifest): Set<string>;
|
|
580
|
+
declare function collectAssessmentIds(manifest: CourseManifest): Set<string>;
|
|
581
|
+
declare function validateFlow(manifest: CourseManifest): ValidationIssue[];
|
|
582
|
+
/** Detect simple goto cycles (same-target chains). */
|
|
583
|
+
declare function detectFlowCycles(flow: FlowRule[]): string[];
|
|
584
|
+
|
|
402
585
|
interface LearnerChoice {
|
|
403
586
|
id: string;
|
|
404
587
|
text: string;
|
|
@@ -414,14 +597,42 @@ interface LearnerAssessment {
|
|
|
414
597
|
passingScore: number;
|
|
415
598
|
questions: LearnerQuestion[];
|
|
416
599
|
}
|
|
600
|
+
interface AssessmentRuntimeConfig {
|
|
601
|
+
maxAttempts: number;
|
|
602
|
+
shuffleChoices: boolean;
|
|
603
|
+
showFeedback: ShowFeedback;
|
|
604
|
+
}
|
|
605
|
+
interface QuestionFeedback {
|
|
606
|
+
[questionId: string]: string | undefined;
|
|
607
|
+
}
|
|
417
608
|
interface RuntimeAssessmentBundle {
|
|
418
609
|
assessments: Record<string, LearnerAssessment>;
|
|
419
610
|
answerKeys: Record<string, Record<string, string>>;
|
|
611
|
+
configs: Record<string, AssessmentRuntimeConfig>;
|
|
612
|
+
feedback: Record<string, QuestionFeedback>;
|
|
420
613
|
}
|
|
421
614
|
declare function toLearnerAssessment(assessment: Assessment): {
|
|
422
615
|
learner: LearnerAssessment;
|
|
423
616
|
answerKey: Record<string, string>;
|
|
617
|
+
config: AssessmentRuntimeConfig;
|
|
618
|
+
feedback: QuestionFeedback;
|
|
424
619
|
};
|
|
620
|
+
|
|
621
|
+
interface ParsedAssessmentsResult {
|
|
622
|
+
parsed: Map<string, Assessment>;
|
|
623
|
+
issues: ValidationIssue[];
|
|
624
|
+
}
|
|
625
|
+
declare function loadParsedAssessments(courseDir: string, manifest: CourseManifest): Promise<ParsedAssessmentsResult>;
|
|
626
|
+
declare function buildRuntimeAssessmentBundleFromParsed(parsed: Map<string, Assessment>): RuntimeAssessmentBundle;
|
|
425
627
|
declare function buildRuntimeAssessmentBundle(courseDir: string, manifest: CourseManifest): Promise<RuntimeAssessmentBundle>;
|
|
426
628
|
|
|
427
|
-
|
|
629
|
+
interface CourseActivity {
|
|
630
|
+
id: string;
|
|
631
|
+
title: string;
|
|
632
|
+
kind: "lesson" | "assessment";
|
|
633
|
+
}
|
|
634
|
+
declare function enumerateActivities(manifest: CourseManifest): CourseActivity[];
|
|
635
|
+
|
|
636
|
+
declare function escapeHtml(text: string): string;
|
|
637
|
+
|
|
638
|
+
export { type Assessment, type AssessmentRef, type AssessmentRuntimeConfig, BUILTIN_COMPONENT_IDS, type BuiltinComponentId, type ComponentLesson, type Condition, type CourseActivity, type CourseManifest, type FlowRule, type LearnerAssessment, type LearnerChoice, type LearnerQuestion, type Lesson, type QuestionFeedback, type RuntimeAssessmentBundle, type ShowFeedback, type ValidationIssue, type ValidationResult, type VariableDef, assertResolvedPathContained, assessmentQuestionSchema, assessmentRefSchema, assessmentSchema, buildRuntimeAssessmentBundle, buildRuntimeAssessmentBundleFromParsed, collectActivityIds, collectAssessmentIds, componentLessonSchema, conditionSchema, courseManifestSchema, detectFlowCycles, enumerateActivities, escapeHtml, flowRuleSchema, formatErrorMessage, formatIssuePath, htmlLessonSchema, isBuiltinComponentId, isPathContained, lessonSchema, loadManifest, loadParsedAssessments, markdownLessonSchema, resolveCoursePath, runtimeConfigSchema, showFeedbackSchema, toLearnerAssessment, trackingSchema, validateCourse, validateFlow, variableDefSchema };
|