@telebort/question-banks 2.2.0 → 2.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.
@@ -1,4 +1,4 @@
1
- import { Q as QuestionType, D as Difficulty, C as Course } from './course-CkP2VX_5.js';
1
+ import { Q as QuestionType, D as Difficulty, C as Course } from './course-DD94UGiI.js';
2
2
 
3
3
  /**
4
4
  * Data source interface - consumers provide their own implementation
@@ -1,4 +1,4 @@
1
- import { Q as QuestionType, D as Difficulty, C as Course } from './course-CkP2VX_5.cjs';
1
+ import { Q as QuestionType, D as Difficulty, C as Course } from './course-DD94UGiI.cjs';
2
2
 
3
3
  /**
4
4
  * Data source interface - consumers provide their own implementation
package/dist/index.cjs CHANGED
@@ -14,22 +14,52 @@ var OptionSchema = zod.z.object({
14
14
  text: zod.z.string().min(1),
15
15
  isCorrect: zod.z.boolean(),
16
16
  // v1.1 fields - optional for backward compatibility
17
- misconceptionId: zod.z.string().optional(),
17
+ misconceptionId: zod.z.string().nullish(),
18
18
  feedback: FeedbackSchema.optional()
19
19
  });
20
20
  var QuestionTypeSchema = zod.z.enum([
21
+ // Core assessment types (original v1.0)
21
22
  "vocabulary",
22
23
  "code_understanding",
23
24
  "problem_solving",
24
25
  "application",
25
- "reflection"
26
+ "reflection",
27
+ // Code-focused types
28
+ "debugging",
29
+ "trace",
30
+ "predict",
31
+ // Higher-order thinking
32
+ "analyze",
33
+ "evaluation",
34
+ "synthesis",
35
+ "design",
36
+ "conceptual",
37
+ "explain",
38
+ // Domain-specific
39
+ "bebras",
40
+ "blockmodel",
41
+ "ethical_reasoning"
26
42
  ]);
27
43
  var QuestionArchetypeSchema = zod.z.enum([
44
+ // Original archetypes (v1.1)
28
45
  "vocabulary",
29
46
  "trace",
30
47
  "bebras",
31
48
  "blockmodel",
32
- "parsons"
49
+ "parsons",
50
+ // Assessment patterns
51
+ "application",
52
+ "problem_solving",
53
+ "debugging",
54
+ "predict",
55
+ "explain",
56
+ "evaluation",
57
+ "reflection",
58
+ "conceptual",
59
+ "analysis",
60
+ "design",
61
+ "synthesis",
62
+ "code"
33
63
  ]);
34
64
  var DifficultySchema = zod.z.enum(["easy", "medium", "hard", "challenge"]);
35
65
  var BloomsTaxonomySchema = zod.z.enum([
@@ -46,6 +76,10 @@ var QuestionMetadataSchema = zod.z.object({
46
76
  // seconds
47
77
  bloomsTaxonomy: BloomsTaxonomySchema,
48
78
  tags: zod.z.array(zod.z.string()),
79
+ conceptTags: zod.z.array(zod.z.string()).optional(),
80
+ // mastery-relevant concept tags
81
+ metaTags: zod.z.array(zod.z.string()).optional(),
82
+ // Bloom's types, course codes, internal
49
83
  source: zod.z.string().default("exit-ticket"),
50
84
  version: zod.z.string().default("1.1"),
51
85
  createdDate: zod.z.string().optional(),
@@ -53,8 +87,8 @@ var QuestionMetadataSchema = zod.z.object({
53
87
  });
54
88
  var QuestionSchema = zod.z.object({
55
89
  // Identifiers
56
- questionId: zod.z.string().regex(/^[a-z0-9-]+-l\d+-q\d+$/),
57
- globalId: zod.z.string().regex(/^exit-ticket-\d{4}$/),
90
+ questionId: zod.z.string().regex(/^[a-z0-9][-a-z0-9]*-(?:l\d+-)?q\d+$/),
91
+ globalId: zod.z.string().regex(/^exit-ticket-[a-z0-9][-a-z0-9]*$/),
58
92
  questionNumber: zod.z.number().int().positive().max(5),
59
93
  // Classification
60
94
  questionType: QuestionTypeSchema,
@@ -88,58 +122,17 @@ var QuestionWithoutAnswerSchema = QuestionSchema.omit({
88
122
  })
89
123
  ).length(4)
90
124
  });
91
- var CourseDomainSchema = zod.z.enum([
92
- // AI & Data Science
93
- "ai_data_science",
94
- "ai_ml_cv",
95
- "ai_generative",
96
- "ai_advanced",
97
- // Web Development
98
- "web_development",
99
- // Mobile Development
100
- "mobile_development",
101
- "mobile",
102
- // Block-based Programming
103
- "block_based",
104
- "python_programming",
105
- "design",
106
- // Foundation
107
- "foundation",
108
- "creative_computing"
109
- ]);
110
- var CourseTierSchema = zod.z.enum([
111
- "foundation",
112
- "intermediate",
113
- "advanced"
114
- ]);
115
- var LessonSchema = zod.z.object({
116
- lessonId: zod.z.string(),
117
- // e.g., "ai-1-lesson-1"
118
- lessonNumber: zod.z.number().int().positive(),
119
- lessonTitle: zod.z.string(),
120
- lessonSlug: zod.z.string().optional(),
121
- totalQuestions: zod.z.number().int().positive().default(5),
122
- questions: zod.z.array(QuestionSchema)
123
- });
124
- var CourseSchema = zod.z.object({
125
- courseId: zod.z.string(),
126
- // e.g., "ai-1"
127
- courseName: zod.z.string(),
128
- // e.g., "AI-1 Data Analysis and Data Science"
129
- courseCode: zod.z.string(),
130
- // e.g., "AI1"
131
- domain: CourseDomainSchema,
132
- tier: CourseTierSchema,
133
- difficulty: zod.z.number().int().min(1).max(5),
134
- totalLessons: zod.z.number().int().positive(),
135
- totalQuestions: zod.z.number().int().positive(),
136
- sourceFile: zod.z.string().optional(),
137
- lessons: zod.z.array(LessonSchema)
138
- });
139
- var CourseSummarySchema = CourseSchema.omit({ lessons: true }).extend({
140
- lessons: zod.z.array(
141
- LessonSchema.omit({ questions: true })
142
- ).optional()
125
+ var AssessmentBlueprintSchema = zod.z.object({
126
+ assessmentId: zod.z.string(),
127
+ // e.g., "cs1-quiz-1"
128
+ title: zod.z.string(),
129
+ // e.g., "Quiz 1"
130
+ description: zod.z.string(),
131
+ lessonRange: zod.z.tuple([zod.z.number().int().positive(), zod.z.number().int().positive()]).refine(([start2, end2]) => start2 <= end2, { message: "lessonRange start must be <= end" }),
132
+ questionCount: zod.z.number().int().positive(),
133
+ passingScore: zod.z.number().min(0).max(100),
134
+ timeLimitMinutes: zod.z.number().int().positive().nullable(),
135
+ tags: zod.z.array(zod.z.string()).optional()
143
136
  });
144
137
  var UserResponseSchema = zod.z.object({
145
138
  questionId: zod.z.string(),
@@ -180,7 +173,7 @@ var MisconceptionReportSchema = zod.z.object({
180
173
  description: zod.z.string().optional()
181
174
  });
182
175
  var GradedAssessmentSchema = zod.z.object({
183
- assessmentId: zod.z.string().uuid(),
176
+ assessmentId: zod.z.string(),
184
177
  userId: zod.z.string().optional(),
185
178
  courseId: zod.z.string(),
186
179
  lessonId: zod.z.string().optional(),
@@ -216,6 +209,74 @@ var ValidationResultSchema = zod.z.object({
216
209
  questionCount: zod.z.number().int(),
217
210
  answeredCount: zod.z.number().int()
218
211
  });
212
+
213
+ // src/schemas/course.ts
214
+ var CourseDomainSchema = zod.z.enum([
215
+ // AI & Data Science
216
+ "ai_data_science",
217
+ "ai_ml_cv",
218
+ "ai_generative",
219
+ "ai_advanced",
220
+ "ai",
221
+ // Prompt Engineering (general AI)
222
+ // Web Development
223
+ "web_development",
224
+ // Mobile Development
225
+ "mobile_development",
226
+ "mobile",
227
+ // Block-based Programming
228
+ "block_based",
229
+ "python_programming",
230
+ "design",
231
+ "artificial_intelligence",
232
+ // Block-based AI courses (BBAI)
233
+ // Computer Science
234
+ "cs_foundations",
235
+ // CS1-CS4 courses
236
+ // Foundation
237
+ "foundation",
238
+ "creative_computing",
239
+ // Utility / Tools
240
+ "tools"
241
+ // IDE Setup, Git, Vibe Coding
242
+ ]);
243
+ var CourseTierSchema = zod.z.enum([
244
+ "beginner",
245
+ // Block-based courses (BBW, BBP, BBD, BBAI)
246
+ "foundation",
247
+ "intermediate",
248
+ "advanced"
249
+ ]);
250
+ var LessonSchema = zod.z.object({
251
+ lessonId: zod.z.string(),
252
+ // e.g., "ai-1-lesson-1"
253
+ lessonNumber: zod.z.number().int().positive(),
254
+ lessonTitle: zod.z.string(),
255
+ lessonSlug: zod.z.string().optional(),
256
+ totalQuestions: zod.z.number().int().positive().default(5),
257
+ questions: zod.z.array(QuestionSchema)
258
+ });
259
+ var CourseSchema = zod.z.object({
260
+ courseId: zod.z.string(),
261
+ // e.g., "ai-1"
262
+ courseName: zod.z.string(),
263
+ // e.g., "AI-1 Data Analysis and Data Science"
264
+ courseCode: zod.z.string(),
265
+ // e.g., "AI1"
266
+ domain: CourseDomainSchema,
267
+ tier: CourseTierSchema,
268
+ difficulty: zod.z.number().int().min(1).max(5),
269
+ totalLessons: zod.z.number().int().positive(),
270
+ totalQuestions: zod.z.number().int().positive(),
271
+ sourceFile: zod.z.string().optional(),
272
+ lessons: zod.z.array(LessonSchema),
273
+ assessments: zod.z.array(AssessmentBlueprintSchema).optional()
274
+ });
275
+ var CourseSummarySchema = CourseSchema.omit({ lessons: true }).extend({
276
+ lessons: zod.z.array(
277
+ LessonSchema.omit({ questions: true })
278
+ ).optional()
279
+ });
219
280
  var CertificationProjectSchema = zod.z.object({
220
281
  projectNumber: zod.z.number().int().positive(),
221
282
  projectName: zod.z.string(),
@@ -259,6 +320,138 @@ var CertificationIndexSchema = zod.z.object({
259
320
  cdnBaseUrl: zod.z.string().url()
260
321
  });
261
322
 
323
+ // src/core/assessment.ts
324
+ var SeededRandom = class {
325
+ constructor(seed) {
326
+ this.state = seed;
327
+ }
328
+ next() {
329
+ this.state = (this.state * 1103515245 + 12345) % 2147483648;
330
+ return this.state / 2147483648;
331
+ }
332
+ /** Fisher-Yates shuffle */
333
+ shuffle(array) {
334
+ const result = [...array];
335
+ for (let i3 = result.length - 1; i3 > 0; i3--) {
336
+ const j2 = Math.floor(this.next() * (i3 + 1));
337
+ const temp = result[i3];
338
+ result[i3] = result[j2];
339
+ result[j2] = temp;
340
+ }
341
+ return result;
342
+ }
343
+ };
344
+ function hashString(str) {
345
+ let hash = 0;
346
+ for (let i3 = 0; i3 < str.length; i3++) {
347
+ const char = str.charCodeAt(i3);
348
+ hash = (hash << 5) - hash + char;
349
+ hash = hash & hash;
350
+ }
351
+ return Math.abs(hash);
352
+ }
353
+ function createAssessmentModule(getCourse) {
354
+ return {
355
+ async getAssessments(courseId) {
356
+ const course = await getCourse(courseId);
357
+ return course?.assessments ?? [];
358
+ },
359
+ async buildAssessmentQuiz(courseId, assessmentId, seed) {
360
+ const course = await getCourse(courseId);
361
+ if (!course) throw new Error(`Course not found: ${courseId}`);
362
+ const blueprint = course.assessments?.find((a2) => a2.assessmentId === assessmentId);
363
+ if (!blueprint) throw new Error(`Assessment not found: ${assessmentId}`);
364
+ const [rangeStart, rangeEnd] = blueprint.lessonRange;
365
+ const pool = [];
366
+ for (const lesson of course.lessons) {
367
+ if (lesson.lessonNumber >= rangeStart && lesson.lessonNumber <= rangeEnd) {
368
+ pool.push(...lesson.questions);
369
+ }
370
+ }
371
+ if (pool.length === 0) {
372
+ throw new Error(`No questions found for lessons ${rangeStart}-${rangeEnd} in ${courseId}`);
373
+ }
374
+ const seedValue = seed ? hashString(seed) : Date.now();
375
+ const rng = new SeededRandom(seedValue);
376
+ const shuffled = rng.shuffle(pool);
377
+ return shuffled.slice(0, Math.min(blueprint.questionCount, shuffled.length));
378
+ },
379
+ async gradeAssessment(courseId, assessmentId, responses, passingScore) {
380
+ const course = await getCourse(courseId);
381
+ if (!course) throw new Error(`Course not found: ${courseId}`);
382
+ const blueprint = course.assessments?.find((a2) => a2.assessmentId === assessmentId);
383
+ if (!blueprint) throw new Error(`Assessment not found: ${assessmentId}`);
384
+ const threshold = passingScore ?? blueprint.passingScore;
385
+ const questionMap = /* @__PURE__ */ new Map();
386
+ for (const lesson of course.lessons) {
387
+ for (const q of lesson.questions) {
388
+ questionMap.set(q.questionId, q);
389
+ }
390
+ }
391
+ const gradedResponses = [];
392
+ let correctCount = 0;
393
+ for (const response of responses) {
394
+ const question = questionMap.get(response.questionId);
395
+ if (!question) continue;
396
+ const selectedOption = question.options.find((o2) => o2.key === response.selectedAnswer);
397
+ const isCorrect = selectedOption?.isCorrect ?? false;
398
+ if (isCorrect) correctCount++;
399
+ gradedResponses.push({
400
+ questionId: response.questionId,
401
+ selectedAnswer: response.selectedAnswer,
402
+ correctAnswer: question.correctAnswer,
403
+ isCorrect,
404
+ misconceptionId: isCorrect ? null : selectedOption?.misconceptionId ?? null,
405
+ feedback: selectedOption?.feedback,
406
+ timeSpent: response.timeSpent
407
+ });
408
+ }
409
+ const totalQuestions = gradedResponses.length;
410
+ const score = totalQuestions > 0 ? Math.round(correctCount / totalQuestions * 100) : 0;
411
+ const misconceptionCounts = /* @__PURE__ */ new Map();
412
+ for (const gr of gradedResponses) {
413
+ if (gr.misconceptionId) {
414
+ const existing = misconceptionCounts.get(gr.misconceptionId);
415
+ if (existing) {
416
+ existing.count++;
417
+ existing.questionIds.push(gr.questionId);
418
+ } else {
419
+ misconceptionCounts.set(gr.misconceptionId, {
420
+ count: 1,
421
+ questionIds: [gr.questionId]
422
+ });
423
+ }
424
+ }
425
+ }
426
+ const misconceptions = Array.from(misconceptionCounts.entries()).map(([id, data]) => ({
427
+ misconceptionId: id,
428
+ count: data.count,
429
+ percentage: Math.round(data.count / totalQuestions * 100),
430
+ questionIds: data.questionIds
431
+ })).sort((a2, b) => b.count - a2.count);
432
+ if (totalQuestions === 0) {
433
+ throw new Error(`No valid questions matched for assessment ${assessmentId}. All ${responses.length} response(s) referenced unknown questionIds.`);
434
+ }
435
+ const now = (/* @__PURE__ */ new Date()).toISOString();
436
+ const generatedId = `grade-${courseId}-${assessmentId}-${hashString(now).toString(36)}`;
437
+ return {
438
+ assessmentId: generatedId,
439
+ courseId,
440
+ totalQuestions,
441
+ correctCount,
442
+ incorrectCount: totalQuestions - correctCount,
443
+ score,
444
+ passed: score >= threshold,
445
+ responses: gradedResponses,
446
+ misconceptions,
447
+ topMisconceptions: misconceptions.slice(0, 3).map((m3) => m3.misconceptionId),
448
+ submittedAt: now,
449
+ gradedAt: now
450
+ };
451
+ }
452
+ };
453
+ }
454
+
262
455
  // src/knowledge/index.ts
263
456
  function createLogger(config) {
264
457
  return {
@@ -23012,6 +23205,7 @@ function createExitTicketsSDK(config) {
23012
23205
  };
23013
23206
  const malaysiaMath = createMalaysiaMathModule(malaysiaMathConfig);
23014
23207
  const malaysiaMathTaxonomy = createMalaysiaMathTaxonomyModule(malaysiaMathConfig);
23208
+ const assessment = createAssessmentModule(data.getCourse);
23015
23209
  return {
23016
23210
  data,
23017
23211
  query,
@@ -23022,11 +23216,13 @@ function createExitTicketsSDK(config) {
23022
23216
  knowledge,
23023
23217
  taxonomy,
23024
23218
  malaysiaMath,
23025
- malaysiaMathTaxonomy
23219
+ malaysiaMathTaxonomy,
23220
+ assessment
23026
23221
  };
23027
23222
  }
23028
23223
  var createQuestionBanksSDK = createExitTicketsSDK;
23029
23224
 
23225
+ exports.AssessmentBlueprintSchema = AssessmentBlueprintSchema;
23030
23226
  exports.AssessmentSubmissionSchema = AssessmentSubmissionSchema;
23031
23227
  exports.BloomLevelSchema = BloomLevelSchema;
23032
23228
  exports.BloomsTaxonomySchema = BloomsTaxonomySchema;
@@ -23077,6 +23273,7 @@ exports.analyzeQuestion = analyzeQuestion;
23077
23273
  exports.analyzeStemEnding = analyzeStemEnding;
23078
23274
  exports.classifyOptionStructure = classifyOptionStructure;
23079
23275
  exports.cosineSimilarity = cosineSimilarity;
23276
+ exports.createAssessmentModule = createAssessmentModule;
23080
23277
  exports.createExitTicketsSDK = createExitTicketsSDK;
23081
23278
  exports.createKnowledgeModule = createKnowledgeModule;
23082
23279
  exports.createMalaysiaMathModule = createMalaysiaMathModule;