@telebort/question-banks 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,452 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // src/schemas/question.ts
6
+ var FeedbackSchema = zod.z.object({
7
+ short: zod.z.string().min(1).max(200),
8
+ detailed: zod.z.string().min(1).max(1e3),
9
+ socraticHint: zod.z.string().max(300).optional()
10
+ });
11
+ var OptionKeySchema = zod.z.enum(["A", "B", "C", "D"]);
12
+ var OptionSchema = zod.z.object({
13
+ key: OptionKeySchema,
14
+ text: zod.z.string().min(1),
15
+ isCorrect: zod.z.boolean(),
16
+ // v1.1 fields - optional for backward compatibility
17
+ misconceptionId: zod.z.string().optional(),
18
+ feedback: FeedbackSchema.optional()
19
+ });
20
+ var QuestionTypeSchema = zod.z.enum([
21
+ "vocabulary",
22
+ "code_understanding",
23
+ "problem_solving",
24
+ "application",
25
+ "reflection"
26
+ ]);
27
+ var QuestionArchetypeSchema = zod.z.enum([
28
+ "vocabulary",
29
+ "trace",
30
+ "bebras",
31
+ "blockmodel",
32
+ "parsons"
33
+ ]);
34
+ var DifficultySchema = zod.z.enum(["easy", "medium", "hard", "challenge"]);
35
+ var BloomsTaxonomySchema = zod.z.enum([
36
+ "remember",
37
+ "understand",
38
+ "apply",
39
+ "analyze",
40
+ "evaluate",
41
+ "create"
42
+ ]);
43
+ var QuestionMetadataSchema = zod.z.object({
44
+ difficulty: DifficultySchema,
45
+ estimatedTime: zod.z.number().int().positive(),
46
+ // seconds
47
+ bloomsTaxonomy: BloomsTaxonomySchema,
48
+ tags: zod.z.array(zod.z.string()),
49
+ source: zod.z.string().default("exit-ticket"),
50
+ version: zod.z.string().default("1.1"),
51
+ createdDate: zod.z.string().optional(),
52
+ lastModified: zod.z.string().optional()
53
+ });
54
+ var QuestionSchema = zod.z.object({
55
+ // Identifiers
56
+ questionId: zod.z.string().regex(/^[a-z0-9-]+-l\d+-q\d+$/),
57
+ globalId: zod.z.string().regex(/^exit-ticket-\d{4}$/),
58
+ questionNumber: zod.z.number().int().positive().max(5),
59
+ // Classification
60
+ questionType: QuestionTypeSchema,
61
+ questionTypeLabel: zod.z.string(),
62
+ questionArchetype: QuestionArchetypeSchema.optional(),
63
+ // v1.1
64
+ // Content
65
+ prompt: zod.z.string().min(10),
66
+ hasCodeBlock: zod.z.boolean(),
67
+ codeLanguage: zod.z.string().nullable(),
68
+ codeContent: zod.z.string().nullable(),
69
+ // Misconception targeting (v1.1)
70
+ misconceptionTargets: zod.z.array(zod.z.string()).optional(),
71
+ // Answer options (exactly 4)
72
+ options: zod.z.array(OptionSchema).length(4),
73
+ // Correct answer
74
+ correctAnswer: OptionKeySchema,
75
+ correctAnswerText: zod.z.string(),
76
+ // Metadata
77
+ metadata: QuestionMetadataSchema
78
+ });
79
+ var QuestionWithoutAnswerSchema = QuestionSchema.omit({
80
+ correctAnswer: true,
81
+ correctAnswerText: true
82
+ }).extend({
83
+ options: zod.z.array(
84
+ OptionSchema.omit({
85
+ isCorrect: true,
86
+ feedback: true,
87
+ misconceptionId: true
88
+ })
89
+ ).length(4)
90
+ });
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()
143
+ });
144
+ var UserResponseSchema = zod.z.object({
145
+ questionId: zod.z.string(),
146
+ selectedAnswer: OptionKeySchema,
147
+ timeSpent: zod.z.number().int().nonnegative().optional(),
148
+ // milliseconds
149
+ submittedAt: zod.z.string().datetime().optional()
150
+ });
151
+ var AssessmentSubmissionSchema = zod.z.object({
152
+ assessmentId: zod.z.string().uuid().optional(),
153
+ // Generated if not provided
154
+ userId: zod.z.string().optional(),
155
+ // For authenticated users
156
+ sessionId: zod.z.string().optional(),
157
+ // For anonymous sessions
158
+ courseId: zod.z.string(),
159
+ lessonId: zod.z.string().optional(),
160
+ responses: zod.z.array(UserResponseSchema).min(1),
161
+ submittedAt: zod.z.string().datetime(),
162
+ metadata: zod.z.record(zod.z.unknown()).optional()
163
+ // Custom metadata
164
+ });
165
+ var GradedResponseSchema = zod.z.object({
166
+ questionId: zod.z.string(),
167
+ selectedAnswer: OptionKeySchema,
168
+ correctAnswer: OptionKeySchema,
169
+ isCorrect: zod.z.boolean(),
170
+ misconceptionId: zod.z.string().nullable(),
171
+ // For incorrect answers
172
+ feedback: FeedbackSchema.optional(),
173
+ timeSpent: zod.z.number().int().nonnegative().optional()
174
+ });
175
+ var MisconceptionReportSchema = zod.z.object({
176
+ misconceptionId: zod.z.string(),
177
+ count: zod.z.number().int().positive(),
178
+ percentage: zod.z.number().min(0).max(100),
179
+ questionIds: zod.z.array(zod.z.string()),
180
+ description: zod.z.string().optional()
181
+ });
182
+ var GradedAssessmentSchema = zod.z.object({
183
+ assessmentId: zod.z.string().uuid(),
184
+ userId: zod.z.string().optional(),
185
+ courseId: zod.z.string(),
186
+ lessonId: zod.z.string().optional(),
187
+ // Scores
188
+ totalQuestions: zod.z.number().int().positive(),
189
+ correctCount: zod.z.number().int().nonnegative(),
190
+ incorrectCount: zod.z.number().int().nonnegative(),
191
+ score: zod.z.number().min(0).max(100),
192
+ // Percentage
193
+ passed: zod.z.boolean(),
194
+ // Based on threshold (default 70%)
195
+ // Detailed results
196
+ responses: zod.z.array(GradedResponseSchema),
197
+ // Misconception analysis (v1.1 feature)
198
+ misconceptions: zod.z.array(MisconceptionReportSchema).optional(),
199
+ topMisconceptions: zod.z.array(zod.z.string()).max(3).optional(),
200
+ // Timing
201
+ totalTimeSpent: zod.z.number().int().nonnegative().optional(),
202
+ // milliseconds
203
+ averageTimePerQuestion: zod.z.number().nonnegative().optional(),
204
+ submittedAt: zod.z.string().datetime(),
205
+ gradedAt: zod.z.string().datetime()
206
+ });
207
+ var ValidationResultSchema = zod.z.object({
208
+ valid: zod.z.boolean(),
209
+ errors: zod.z.array(zod.z.object({
210
+ path: zod.z.array(zod.z.union([zod.z.string(), zod.z.number()])),
211
+ message: zod.z.string(),
212
+ code: zod.z.string().optional()
213
+ })).optional(),
214
+ isComplete: zod.z.boolean(),
215
+ // All questions answered
216
+ questionCount: zod.z.number().int(),
217
+ answeredCount: zod.z.number().int()
218
+ });
219
+
220
+ // src/index.ts
221
+ function createExitTicketsSDK(config) {
222
+ let courses = null;
223
+ const questionsById = /* @__PURE__ */ new Map();
224
+ const questionsByGlobalId = /* @__PURE__ */ new Map();
225
+ async function ensureLoaded() {
226
+ if (courses === null) {
227
+ courses = await config.dataSource.loadCourses();
228
+ for (const course of courses) {
229
+ for (const lesson of course.lessons) {
230
+ for (const question of lesson.questions) {
231
+ questionsById.set(question.questionId, question);
232
+ questionsByGlobalId.set(question.globalId, question.questionId);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ return courses;
238
+ }
239
+ const data = {
240
+ async getCourses() {
241
+ return ensureLoaded();
242
+ },
243
+ async getCourse(courseId) {
244
+ const all = await ensureLoaded();
245
+ return all.find((c) => c.courseId === courseId) ?? null;
246
+ },
247
+ async getLesson(courseId, lessonNumber) {
248
+ const course = await data.getCourse(courseId);
249
+ return course?.lessons.find((l) => l.lessonNumber === lessonNumber) ?? null;
250
+ },
251
+ async getQuestion(questionId) {
252
+ await ensureLoaded();
253
+ return questionsById.get(questionId) ?? null;
254
+ },
255
+ async getAllQuestions() {
256
+ await ensureLoaded();
257
+ return Array.from(questionsById.values());
258
+ }
259
+ };
260
+ const query = {
261
+ async filterQuestions(filters) {
262
+ const all = await data.getAllQuestions();
263
+ let filtered = all;
264
+ if (filters.courseId) {
265
+ filtered = filtered.filter((q) => q.questionId.startsWith(filters.courseId));
266
+ }
267
+ if (filters.lessonNumber) {
268
+ filtered = filtered.filter((q) => q.questionId.includes(`-l${filters.lessonNumber}-`));
269
+ }
270
+ if (filters.questionType) {
271
+ filtered = filtered.filter((q) => q.questionType === filters.questionType);
272
+ }
273
+ if (filters.difficulty) {
274
+ filtered = filtered.filter((q) => q.metadata.difficulty === filters.difficulty);
275
+ }
276
+ if (filters.hasCodeBlock !== void 0) {
277
+ filtered = filtered.filter((q) => q.hasCodeBlock === filters.hasCodeBlock);
278
+ }
279
+ if (filters.tags?.length) {
280
+ filtered = filtered.filter(
281
+ (q) => filters.tags.some((tag) => q.metadata.tags.includes(tag))
282
+ );
283
+ }
284
+ const total = filtered.length;
285
+ const offset = filters.offset ?? 0;
286
+ const limit = filters.limit ?? 20;
287
+ const paginated = filtered.slice(offset, offset + limit);
288
+ return {
289
+ data: paginated,
290
+ total,
291
+ limit,
292
+ offset,
293
+ hasMore: offset + limit < total
294
+ };
295
+ },
296
+ async getRandomQuestions(count, filters) {
297
+ const result = await query.filterQuestions({ ...filters, limit: 1e3 });
298
+ const shuffled = [...result.data].sort(() => Math.random() - 0.5);
299
+ return shuffled.slice(0, count);
300
+ }
301
+ };
302
+ const quiz = {
303
+ async generateQuiz(criteria) {
304
+ const questions = await query.getRandomQuestions(criteria.questionCount, {
305
+ courseId: criteria.courseIds?.[0],
306
+ difficulty: criteria.difficulty,
307
+ questionType: criteria.questionTypes?.[0],
308
+ hasCodeBlock: criteria.includeCodeBlocks
309
+ });
310
+ return {
311
+ questions,
312
+ metadata: {
313
+ distribution: questions.reduce((acc, q) => {
314
+ acc[q.questionType] = (acc[q.questionType] ?? 0) + 1;
315
+ return acc;
316
+ }, {}),
317
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
318
+ }
319
+ };
320
+ },
321
+ async getLessonQuiz(courseId, lessonNumber) {
322
+ const lesson = await data.getLesson(courseId, lessonNumber);
323
+ return lesson?.questions ?? [];
324
+ }
325
+ };
326
+ const search = {
327
+ async search(options) {
328
+ const all = await data.getAllQuestions();
329
+ const query2 = options.query.toLowerCase();
330
+ const scored = all.map((q) => {
331
+ let score = 0;
332
+ if (q.prompt.toLowerCase().includes(query2)) score += 3;
333
+ if (q.options.some((o) => o.text.toLowerCase().includes(query2))) score += 1;
334
+ return { ...q, relevanceScore: score };
335
+ }).filter((q) => q.relevanceScore > 0).sort((a, b) => b.relevanceScore - a.relevanceScore);
336
+ const offset = options.offset ?? 0;
337
+ const limit = options.limit ?? 20;
338
+ return {
339
+ data: scored.slice(offset, offset + limit),
340
+ total: scored.length,
341
+ limit,
342
+ offset,
343
+ hasMore: offset + limit < scored.length
344
+ };
345
+ },
346
+ async searchByTags(tags, matchAll = false) {
347
+ const all = await data.getAllQuestions();
348
+ return all.filter((q) => {
349
+ if (matchAll) {
350
+ return tags.every((tag) => q.metadata.tags.includes(tag));
351
+ }
352
+ return tags.some((tag) => q.metadata.tags.includes(tag));
353
+ });
354
+ }
355
+ };
356
+ const analytics = {
357
+ async getStatistics() {
358
+ const all = await ensureLoaded();
359
+ const questions = await data.getAllQuestions();
360
+ const stats = {
361
+ totalCourses: all.length,
362
+ totalLessons: all.reduce((sum, c) => sum + c.lessons.length, 0),
363
+ totalQuestions: questions.length,
364
+ byDifficulty: {},
365
+ byQuestionType: {},
366
+ byBloomsTaxonomy: {},
367
+ withCodeBlocks: 0,
368
+ withoutCodeBlocks: 0
369
+ };
370
+ for (const q of questions) {
371
+ stats.byDifficulty[q.metadata.difficulty] = (stats.byDifficulty[q.metadata.difficulty] ?? 0) + 1;
372
+ stats.byQuestionType[q.questionType] = (stats.byQuestionType[q.questionType] ?? 0) + 1;
373
+ stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] = (stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] ?? 0) + 1;
374
+ if (q.hasCodeBlock) stats.withCodeBlocks++;
375
+ else stats.withoutCodeBlocks++;
376
+ }
377
+ return stats;
378
+ },
379
+ async getCourseStatistics(courseId) {
380
+ const course = await data.getCourse(courseId);
381
+ if (!course) throw new Error(`Course not found: ${courseId}`);
382
+ const questions = course.lessons.flatMap((l) => l.questions);
383
+ const stats = {
384
+ courseId,
385
+ totalCourses: 1,
386
+ totalLessons: course.lessons.length,
387
+ totalQuestions: questions.length,
388
+ byDifficulty: {},
389
+ byQuestionType: {},
390
+ byBloomsTaxonomy: {},
391
+ withCodeBlocks: 0,
392
+ withoutCodeBlocks: 0
393
+ };
394
+ for (const q of questions) {
395
+ stats.byDifficulty[q.metadata.difficulty] = (stats.byDifficulty[q.metadata.difficulty] ?? 0) + 1;
396
+ stats.byQuestionType[q.questionType] = (stats.byQuestionType[q.questionType] ?? 0) + 1;
397
+ stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] = (stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] ?? 0) + 1;
398
+ if (q.hasCodeBlock) stats.withCodeBlocks++;
399
+ else stats.withoutCodeBlocks++;
400
+ }
401
+ return stats;
402
+ }
403
+ };
404
+ const validate = {
405
+ async checkAnswer(questionId, answer, mode) {
406
+ const question = await data.getQuestion(questionId);
407
+ if (!question) throw new Error(`Question not found: ${questionId}`);
408
+ const selectedOption = question.options.find((o) => o.key === answer);
409
+ if (!selectedOption) throw new Error(`Invalid answer: ${answer}`);
410
+ const isCorrect = selectedOption.isCorrect;
411
+ return {
412
+ isCorrect,
413
+ feedback: selectedOption.feedback,
414
+ misconceptionId: !isCorrect ? selectedOption.misconceptionId : null,
415
+ correctAnswer: mode === "formative" ? question.correctAnswer : void 0
416
+ };
417
+ }
418
+ };
419
+ return {
420
+ data,
421
+ query,
422
+ quiz,
423
+ search,
424
+ analytics,
425
+ validate
426
+ };
427
+ }
428
+
429
+ exports.AssessmentSubmissionSchema = AssessmentSubmissionSchema;
430
+ exports.BloomsTaxonomySchema = BloomsTaxonomySchema;
431
+ exports.CourseDomainSchema = CourseDomainSchema;
432
+ exports.CourseSchema = CourseSchema;
433
+ exports.CourseSummarySchema = CourseSummarySchema;
434
+ exports.CourseTierSchema = CourseTierSchema;
435
+ exports.DifficultySchema = DifficultySchema;
436
+ exports.FeedbackSchema = FeedbackSchema;
437
+ exports.GradedAssessmentSchema = GradedAssessmentSchema;
438
+ exports.GradedResponseSchema = GradedResponseSchema;
439
+ exports.LessonSchema = LessonSchema;
440
+ exports.MisconceptionReportSchema = MisconceptionReportSchema;
441
+ exports.OptionKeySchema = OptionKeySchema;
442
+ exports.OptionSchema = OptionSchema;
443
+ exports.QuestionArchetypeSchema = QuestionArchetypeSchema;
444
+ exports.QuestionMetadataSchema = QuestionMetadataSchema;
445
+ exports.QuestionSchema = QuestionSchema;
446
+ exports.QuestionTypeSchema = QuestionTypeSchema;
447
+ exports.QuestionWithoutAnswerSchema = QuestionWithoutAnswerSchema;
448
+ exports.UserResponseSchema = UserResponseSchema;
449
+ exports.ValidationResultSchema = ValidationResultSchema;
450
+ exports.createExitTicketsSDK = createExitTicketsSDK;
451
+ //# sourceMappingURL=index.cjs.map
452
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/question.ts","../src/schemas/course.ts","../src/schemas/assessment.ts","../src/index.ts"],"names":["z","query"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod'\n\n// ============================================================================\n// Feedback Schema (v1.1 enhancement)\n// ============================================================================\n\n/**\n * Structured feedback following the \"Not Yet\" protocol\n * - short: Brief acknowledgment (1 sentence)\n * - detailed: Full explanation of the misconception\n * - socraticHint: Optional guiding question for discovery learning\n */\nexport const FeedbackSchema = z.object({\n short: z.string().min(1).max(200),\n detailed: z.string().min(1).max(1000),\n socraticHint: z.string().max(300).optional(),\n})\n\nexport type Feedback = z.infer<typeof FeedbackSchema>\n\n// ============================================================================\n// Option Schema\n// ============================================================================\n\nexport const OptionKeySchema = z.enum(['A', 'B', 'C', 'D'])\n\nexport type OptionKey = z.infer<typeof OptionKeySchema>\n\n/**\n * Answer option with optional misconception tracking (v1.1)\n */\nexport const OptionSchema = z.object({\n key: OptionKeySchema,\n text: z.string().min(1),\n isCorrect: z.boolean(),\n // v1.1 fields - optional for backward compatibility\n misconceptionId: z.string().optional(),\n feedback: FeedbackSchema.optional(),\n})\n\nexport type Option = z.infer<typeof OptionSchema>\n\n// ============================================================================\n// Question Type Enums\n// ============================================================================\n\nexport const QuestionTypeSchema = z.enum([\n 'vocabulary',\n 'code_understanding',\n 'problem_solving',\n 'application',\n 'reflection',\n])\n\nexport type QuestionType = z.infer<typeof QuestionTypeSchema>\n\n/**\n * Question archetype for pedagogical classification\n * - vocabulary: Definition/terminology questions\n * - trace: Code tracing (mental execution)\n * - bebras: Logic-first, computational thinking\n * - blockmodel: Surface/flow/purpose analysis\n * - parsons: Code block reordering (future)\n */\nexport const QuestionArchetypeSchema = z.enum([\n 'vocabulary',\n 'trace',\n 'bebras',\n 'blockmodel',\n 'parsons',\n])\n\nexport type QuestionArchetype = z.infer<typeof QuestionArchetypeSchema>\n\nexport const DifficultySchema = z.enum(['easy', 'medium', 'hard', 'challenge'])\n\nexport type Difficulty = z.infer<typeof DifficultySchema>\n\nexport const BloomsTaxonomySchema = z.enum([\n 'remember',\n 'understand',\n 'apply',\n 'analyze',\n 'evaluate',\n 'create',\n])\n\nexport type BloomsTaxonomy = z.infer<typeof BloomsTaxonomySchema>\n\n// ============================================================================\n// Question Metadata Schema\n// ============================================================================\n\nexport const QuestionMetadataSchema = z.object({\n difficulty: DifficultySchema,\n estimatedTime: z.number().int().positive(), // seconds\n bloomsTaxonomy: BloomsTaxonomySchema,\n tags: z.array(z.string()),\n source: z.string().default('exit-ticket'),\n version: z.string().default('1.1'),\n createdDate: z.string().optional(),\n lastModified: z.string().optional(),\n})\n\nexport type QuestionMetadata = z.infer<typeof QuestionMetadataSchema>\n\n// ============================================================================\n// Question Schema (v1.1)\n// ============================================================================\n\n/**\n * Full question schema with v1.1 enhancements:\n * - questionArchetype: Pedagogical classification\n * - misconceptionTargets: Expected misconceptions this question probes\n * - options with misconceptionId and structured feedback\n */\nexport const QuestionSchema = z.object({\n // Identifiers\n questionId: z.string().regex(/^[a-z0-9-]+-l\\d+-q\\d+$/),\n globalId: z.string().regex(/^exit-ticket-\\d{4}$/),\n questionNumber: z.number().int().positive().max(5),\n\n // Classification\n questionType: QuestionTypeSchema,\n questionTypeLabel: z.string(),\n questionArchetype: QuestionArchetypeSchema.optional(), // v1.1\n\n // Content\n prompt: z.string().min(10),\n hasCodeBlock: z.boolean(),\n codeLanguage: z.string().nullable(),\n codeContent: z.string().nullable(),\n\n // Misconception targeting (v1.1)\n misconceptionTargets: z.array(z.string()).optional(),\n\n // Answer options (exactly 4)\n options: z.array(OptionSchema).length(4),\n\n // Correct answer\n correctAnswer: OptionKeySchema,\n correctAnswerText: z.string(),\n\n // Metadata\n metadata: QuestionMetadataSchema,\n})\n\nexport type Question = z.infer<typeof QuestionSchema>\n\n// ============================================================================\n// Question without answer (for Standard tier - no answer leakage)\n// ============================================================================\n\nexport const QuestionWithoutAnswerSchema = QuestionSchema.omit({\n correctAnswer: true,\n correctAnswerText: true,\n}).extend({\n options: z.array(\n OptionSchema.omit({\n isCorrect: true,\n feedback: true,\n misconceptionId: true,\n })\n ).length(4),\n})\n\nexport type QuestionWithoutAnswer = z.infer<typeof QuestionWithoutAnswerSchema>\n","import { z } from 'zod'\nimport { QuestionSchema } from './question'\n\n// ============================================================================\n// Course Domain & Tier\n// ============================================================================\n\nexport const CourseDomainSchema = z.enum([\n // AI & Data Science\n 'ai_data_science',\n 'ai_ml_cv',\n 'ai_generative',\n 'ai_advanced',\n // Web Development\n 'web_development',\n // Mobile Development\n 'mobile_development',\n 'mobile',\n // Block-based Programming\n 'block_based',\n 'python_programming',\n 'design',\n // Foundation\n 'foundation',\n 'creative_computing',\n])\n\nexport type CourseDomain = z.infer<typeof CourseDomainSchema>\n\nexport const CourseTierSchema = z.enum([\n 'foundation',\n 'intermediate',\n 'advanced',\n])\n\nexport type CourseTier = z.infer<typeof CourseTierSchema>\n\n// ============================================================================\n// Lesson Schema\n// ============================================================================\n\nexport const LessonSchema = z.object({\n lessonId: z.string(), // e.g., \"ai-1-lesson-1\"\n lessonNumber: z.number().int().positive(),\n lessonTitle: z.string(),\n lessonSlug: z.string().optional(),\n totalQuestions: z.number().int().positive().default(5),\n questions: z.array(QuestionSchema),\n})\n\nexport type Lesson = z.infer<typeof LessonSchema>\n\n// ============================================================================\n// Course Schema\n// ============================================================================\n\nexport const CourseSchema = z.object({\n courseId: z.string(), // e.g., \"ai-1\"\n courseName: z.string(), // e.g., \"AI-1 Data Analysis and Data Science\"\n courseCode: z.string(), // e.g., \"AI1\"\n domain: CourseDomainSchema,\n tier: CourseTierSchema,\n difficulty: z.number().int().min(1).max(5),\n totalLessons: z.number().int().positive(),\n totalQuestions: z.number().int().positive(),\n sourceFile: z.string().optional(),\n lessons: z.array(LessonSchema),\n})\n\nexport type Course = z.infer<typeof CourseSchema>\n\n// ============================================================================\n// Course Summary (without questions - for listing)\n// ============================================================================\n\nexport const CourseSummarySchema = CourseSchema.omit({ lessons: true }).extend({\n lessons: z.array(\n LessonSchema.omit({ questions: true })\n ).optional(),\n})\n\nexport type CourseSummary = z.infer<typeof CourseSummarySchema>\n","import { z } from 'zod'\nimport { OptionKeySchema, FeedbackSchema } from './question'\n\n// ============================================================================\n// User Response Schema\n// ============================================================================\n\n/**\n * A single user response to a question\n */\nexport const UserResponseSchema = z.object({\n questionId: z.string(),\n selectedAnswer: OptionKeySchema,\n timeSpent: z.number().int().nonnegative().optional(), // milliseconds\n submittedAt: z.string().datetime().optional(),\n})\n\nexport type UserResponse = z.infer<typeof UserResponseSchema>\n\n// ============================================================================\n// Assessment Submission Schema\n// ============================================================================\n\n/**\n * An assessment submission containing multiple responses\n */\nexport const AssessmentSubmissionSchema = z.object({\n assessmentId: z.string().uuid().optional(), // Generated if not provided\n userId: z.string().optional(), // For authenticated users\n sessionId: z.string().optional(), // For anonymous sessions\n courseId: z.string(),\n lessonId: z.string().optional(),\n responses: z.array(UserResponseSchema).min(1),\n submittedAt: z.string().datetime(),\n metadata: z.record(z.unknown()).optional(), // Custom metadata\n})\n\nexport type AssessmentSubmission = z.infer<typeof AssessmentSubmissionSchema>\n\n// ============================================================================\n// Graded Response Schema (Premium tier)\n// ============================================================================\n\n/**\n * Result of grading a single response\n */\nexport const GradedResponseSchema = z.object({\n questionId: z.string(),\n selectedAnswer: OptionKeySchema,\n correctAnswer: OptionKeySchema,\n isCorrect: z.boolean(),\n misconceptionId: z.string().nullable(), // For incorrect answers\n feedback: FeedbackSchema.optional(),\n timeSpent: z.number().int().nonnegative().optional(),\n})\n\nexport type GradedResponse = z.infer<typeof GradedResponseSchema>\n\n// ============================================================================\n// Misconception Report Schema (Premium tier)\n// ============================================================================\n\n/**\n * Aggregated misconception analysis for an assessment\n */\nexport const MisconceptionReportSchema = z.object({\n misconceptionId: z.string(),\n count: z.number().int().positive(),\n percentage: z.number().min(0).max(100),\n questionIds: z.array(z.string()),\n description: z.string().optional(),\n})\n\nexport type MisconceptionReport = z.infer<typeof MisconceptionReportSchema>\n\n// ============================================================================\n// Graded Assessment Schema (Premium tier)\n// ============================================================================\n\n/**\n * Complete graded assessment with scores and misconception analysis\n */\nexport const GradedAssessmentSchema = z.object({\n assessmentId: z.string().uuid(),\n userId: z.string().optional(),\n courseId: z.string(),\n lessonId: z.string().optional(),\n\n // Scores\n totalQuestions: z.number().int().positive(),\n correctCount: z.number().int().nonnegative(),\n incorrectCount: z.number().int().nonnegative(),\n score: z.number().min(0).max(100), // Percentage\n passed: z.boolean(), // Based on threshold (default 70%)\n\n // Detailed results\n responses: z.array(GradedResponseSchema),\n\n // Misconception analysis (v1.1 feature)\n misconceptions: z.array(MisconceptionReportSchema).optional(),\n topMisconceptions: z.array(z.string()).max(3).optional(),\n\n // Timing\n totalTimeSpent: z.number().int().nonnegative().optional(), // milliseconds\n averageTimePerQuestion: z.number().nonnegative().optional(),\n submittedAt: z.string().datetime(),\n gradedAt: z.string().datetime(),\n})\n\nexport type GradedAssessment = z.infer<typeof GradedAssessmentSchema>\n\n// ============================================================================\n// Validation Result Schema (Standard tier - no answer leakage)\n// ============================================================================\n\n/**\n * Result of validating an assessment submission\n * Does NOT reveal correct answers - only schema compliance\n */\nexport const ValidationResultSchema = z.object({\n valid: z.boolean(),\n errors: z.array(z.object({\n path: z.array(z.union([z.string(), z.number()])),\n message: z.string(),\n code: z.string().optional(),\n })).optional(),\n isComplete: z.boolean(), // All questions answered\n questionCount: z.number().int(),\n answeredCount: z.number().int(),\n})\n\nexport type ValidationResult = z.infer<typeof ValidationResultSchema>\n","/**\n * @telebort/exit-tickets - Exit Tickets SDK\n *\n * Three-tier access to exit ticket questions and assessments:\n *\n * ## Free Tier - Schemas\n * ```typescript\n * import { QuestionSchema } from '@telebort/exit-tickets/schemas'\n * ```\n *\n * ## Standard Tier - Validation API\n * ```typescript\n * import { ValidationClient } from '@telebort/exit-tickets/validation'\n * ```\n *\n * ## Premium Tier - Full Access\n * ```typescript\n * import { PremiumClient } from '@telebort/exit-tickets/premium'\n * ```\n *\n * ## SDK Factory (for custom data sources)\n * ```typescript\n * import { createExitTicketsSDK } from '@telebort/exit-tickets'\n *\n * const sdk = createExitTicketsSDK({\n * dataSource: {\n * async loadCourses() {\n * return myQuestionData\n * }\n * }\n * })\n * ```\n */\n\n// Re-export schemas (Free Tier)\nexport * from './schemas'\n\n// Re-export types\nexport * from './types'\n\n// Re-export core interfaces\nexport * from './core'\n\n// SDK Factory\nimport type { Course, Question, Lesson } from './schemas'\nimport type { ExitTicketsConfig, QuestionFilters, QuizCriteria, SearchOptions, Statistics } from './types'\nimport type { QueryResult } from './core/interfaces'\n\n/**\n * Exit Tickets SDK instance\n */\nexport interface ExitTicketsSDK {\n /**\n * Data access methods\n */\n data: {\n getCourses(): Promise<Course[]>\n getCourse(courseId: string): Promise<Course | null>\n getLesson(courseId: string, lessonNumber: number): Promise<Lesson | null>\n getQuestion(questionId: string): Promise<Question | null>\n getAllQuestions(): Promise<Question[]>\n }\n\n /**\n * Query methods\n */\n query: {\n filterQuestions(filters: QuestionFilters): Promise<QueryResult<Question>>\n getRandomQuestions(count: number, filters?: QuestionFilters): Promise<Question[]>\n }\n\n /**\n * Quiz generation\n */\n quiz: {\n generateQuiz(criteria: QuizCriteria): Promise<{ questions: Question[]; metadata: Record<string, unknown> }>\n getLessonQuiz(courseId: string, lessonNumber: number): Promise<Question[]>\n }\n\n /**\n * Search\n */\n search: {\n search(options: SearchOptions): Promise<QueryResult<Question & { relevanceScore: number }>>\n searchByTags(tags: string[], matchAll?: boolean): Promise<Question[]>\n }\n\n /**\n * Analytics\n */\n analytics: {\n getStatistics(): Promise<Statistics>\n getCourseStatistics(courseId: string): Promise<Statistics & { courseId: string }>\n }\n\n /**\n * Answer validation (requires question data loaded)\n */\n validate: {\n checkAnswer(\n questionId: string,\n answer: string,\n mode: 'formative' | 'summative'\n ): Promise<{\n isCorrect: boolean\n feedback?: { short: string; detailed?: string; socraticHint?: string }\n misconceptionId?: string | null\n correctAnswer?: string\n }>\n }\n}\n\n/**\n * Create an Exit Tickets SDK instance\n *\n * @param config - SDK configuration with data source\n * @returns SDK instance with all services\n *\n * @example\n * ```typescript\n * // Using static JSON import\n * import coursesData from './data/courses.json'\n *\n * const sdk = createExitTicketsSDK({\n * dataSource: {\n * async loadCourses() {\n * return coursesData\n * }\n * }\n * })\n *\n * const questions = await sdk.query.filterQuestions({ courseId: 'ai-1' })\n * ```\n */\nexport function createExitTicketsSDK(config: ExitTicketsConfig): ExitTicketsSDK {\n // Internal state\n let courses: Course[] | null = null\n const questionsById = new Map<string, Question>()\n const questionsByGlobalId = new Map<string, string>()\n // Note: config.passingThreshold is available for future quiz scoring features\n\n // Helper: Ensure data is loaded\n async function ensureLoaded(): Promise<Course[]> {\n if (courses === null) {\n courses = await config.dataSource.loadCourses()\n\n // Build indexes\n for (const course of courses) {\n for (const lesson of course.lessons) {\n for (const question of lesson.questions) {\n questionsById.set(question.questionId, question)\n questionsByGlobalId.set(question.globalId, question.questionId)\n }\n }\n }\n }\n return courses\n }\n\n // Data access methods\n const data: ExitTicketsSDK['data'] = {\n async getCourses() {\n return ensureLoaded()\n },\n\n async getCourse(courseId) {\n const all = await ensureLoaded()\n return all.find(c => c.courseId === courseId) ?? null\n },\n\n async getLesson(courseId, lessonNumber) {\n const course = await data.getCourse(courseId)\n return course?.lessons.find(l => l.lessonNumber === lessonNumber) ?? null\n },\n\n async getQuestion(questionId) {\n await ensureLoaded()\n return questionsById.get(questionId) ?? null\n },\n\n async getAllQuestions() {\n await ensureLoaded()\n return Array.from(questionsById.values())\n },\n }\n\n // Query methods\n const query: ExitTicketsSDK['query'] = {\n async filterQuestions(filters) {\n const all = await data.getAllQuestions()\n let filtered = all\n\n if (filters.courseId) {\n filtered = filtered.filter(q => q.questionId.startsWith(filters.courseId!))\n }\n if (filters.lessonNumber) {\n filtered = filtered.filter(q => q.questionId.includes(`-l${filters.lessonNumber}-`))\n }\n if (filters.questionType) {\n filtered = filtered.filter(q => q.questionType === filters.questionType)\n }\n if (filters.difficulty) {\n filtered = filtered.filter(q => q.metadata.difficulty === filters.difficulty)\n }\n if (filters.hasCodeBlock !== undefined) {\n filtered = filtered.filter(q => q.hasCodeBlock === filters.hasCodeBlock)\n }\n if (filters.tags?.length) {\n filtered = filtered.filter(q =>\n filters.tags!.some(tag => q.metadata.tags.includes(tag))\n )\n }\n\n const total = filtered.length\n const offset = filters.offset ?? 0\n const limit = filters.limit ?? 20\n const paginated = filtered.slice(offset, offset + limit)\n\n return {\n data: paginated,\n total,\n limit,\n offset,\n hasMore: offset + limit < total,\n }\n },\n\n async getRandomQuestions(count, filters) {\n const result = await query.filterQuestions({ ...filters, limit: 1000 })\n const shuffled = [...result.data].sort(() => Math.random() - 0.5)\n return shuffled.slice(0, count)\n },\n }\n\n // Quiz generation\n const quiz: ExitTicketsSDK['quiz'] = {\n async generateQuiz(criteria) {\n const questions = await query.getRandomQuestions(criteria.questionCount, {\n courseId: criteria.courseIds?.[0],\n difficulty: criteria.difficulty,\n questionType: criteria.questionTypes?.[0],\n hasCodeBlock: criteria.includeCodeBlocks,\n })\n\n return {\n questions,\n metadata: {\n distribution: questions.reduce((acc, q) => {\n acc[q.questionType] = (acc[q.questionType] ?? 0) + 1\n return acc\n }, {} as Record<string, number>),\n timestamp: new Date().toISOString(),\n },\n }\n },\n\n async getLessonQuiz(courseId, lessonNumber) {\n const lesson = await data.getLesson(courseId, lessonNumber)\n return lesson?.questions ?? []\n },\n }\n\n // Search\n const search: ExitTicketsSDK['search'] = {\n async search(options) {\n const all = await data.getAllQuestions()\n const query = options.query.toLowerCase()\n\n const scored = all\n .map(q => {\n let score = 0\n if (q.prompt.toLowerCase().includes(query)) score += 3\n if (q.options.some(o => o.text.toLowerCase().includes(query))) score += 1\n return { ...q, relevanceScore: score }\n })\n .filter(q => q.relevanceScore > 0)\n .sort((a, b) => b.relevanceScore - a.relevanceScore)\n\n const offset = options.offset ?? 0\n const limit = options.limit ?? 20\n\n return {\n data: scored.slice(offset, offset + limit),\n total: scored.length,\n limit,\n offset,\n hasMore: offset + limit < scored.length,\n }\n },\n\n async searchByTags(tags, matchAll = false) {\n const all = await data.getAllQuestions()\n return all.filter(q => {\n if (matchAll) {\n return tags.every(tag => q.metadata.tags.includes(tag))\n }\n return tags.some(tag => q.metadata.tags.includes(tag))\n })\n },\n }\n\n // Analytics\n const analytics: ExitTicketsSDK['analytics'] = {\n async getStatistics() {\n const all = await ensureLoaded()\n const questions = await data.getAllQuestions()\n\n const stats: Statistics = {\n totalCourses: all.length,\n totalLessons: all.reduce((sum, c) => sum + c.lessons.length, 0),\n totalQuestions: questions.length,\n byDifficulty: {},\n byQuestionType: {},\n byBloomsTaxonomy: {},\n withCodeBlocks: 0,\n withoutCodeBlocks: 0,\n }\n\n for (const q of questions) {\n stats.byDifficulty[q.metadata.difficulty] = (stats.byDifficulty[q.metadata.difficulty] ?? 0) + 1\n stats.byQuestionType[q.questionType] = (stats.byQuestionType[q.questionType] ?? 0) + 1\n stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] = (stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] ?? 0) + 1\n if (q.hasCodeBlock) stats.withCodeBlocks++\n else stats.withoutCodeBlocks++\n }\n\n return stats\n },\n\n async getCourseStatistics(courseId) {\n const course = await data.getCourse(courseId)\n if (!course) throw new Error(`Course not found: ${courseId}`)\n\n const questions = course.lessons.flatMap(l => l.questions)\n\n const stats = {\n courseId,\n totalCourses: 1,\n totalLessons: course.lessons.length,\n totalQuestions: questions.length,\n byDifficulty: {} as Record<string, number>,\n byQuestionType: {} as Record<string, number>,\n byBloomsTaxonomy: {} as Record<string, number>,\n withCodeBlocks: 0,\n withoutCodeBlocks: 0,\n }\n\n for (const q of questions) {\n stats.byDifficulty[q.metadata.difficulty] = (stats.byDifficulty[q.metadata.difficulty] ?? 0) + 1\n stats.byQuestionType[q.questionType] = (stats.byQuestionType[q.questionType] ?? 0) + 1\n stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] = (stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] ?? 0) + 1\n if (q.hasCodeBlock) stats.withCodeBlocks++\n else stats.withoutCodeBlocks++\n }\n\n return stats\n },\n }\n\n // Validation\n const validate: ExitTicketsSDK['validate'] = {\n async checkAnswer(questionId, answer, mode) {\n const question = await data.getQuestion(questionId)\n if (!question) throw new Error(`Question not found: ${questionId}`)\n\n const selectedOption = question.options.find(o => o.key === answer)\n if (!selectedOption) throw new Error(`Invalid answer: ${answer}`)\n\n const isCorrect = selectedOption.isCorrect\n\n return {\n isCorrect,\n feedback: selectedOption.feedback,\n misconceptionId: !isCorrect ? selectedOption.misconceptionId : null,\n correctAnswer: mode === 'formative' ? question.correctAnswer : undefined,\n }\n },\n }\n\n return {\n data,\n query,\n quiz,\n search,\n analytics,\n validate,\n }\n}\n"]}