@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.js ADDED
@@ -0,0 +1,429 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/schemas/question.ts
4
+ var FeedbackSchema = z.object({
5
+ short: z.string().min(1).max(200),
6
+ detailed: z.string().min(1).max(1e3),
7
+ socraticHint: z.string().max(300).optional()
8
+ });
9
+ var OptionKeySchema = z.enum(["A", "B", "C", "D"]);
10
+ var OptionSchema = z.object({
11
+ key: OptionKeySchema,
12
+ text: z.string().min(1),
13
+ isCorrect: z.boolean(),
14
+ // v1.1 fields - optional for backward compatibility
15
+ misconceptionId: z.string().optional(),
16
+ feedback: FeedbackSchema.optional()
17
+ });
18
+ var QuestionTypeSchema = z.enum([
19
+ "vocabulary",
20
+ "code_understanding",
21
+ "problem_solving",
22
+ "application",
23
+ "reflection"
24
+ ]);
25
+ var QuestionArchetypeSchema = z.enum([
26
+ "vocabulary",
27
+ "trace",
28
+ "bebras",
29
+ "blockmodel",
30
+ "parsons"
31
+ ]);
32
+ var DifficultySchema = z.enum(["easy", "medium", "hard", "challenge"]);
33
+ var BloomsTaxonomySchema = z.enum([
34
+ "remember",
35
+ "understand",
36
+ "apply",
37
+ "analyze",
38
+ "evaluate",
39
+ "create"
40
+ ]);
41
+ var QuestionMetadataSchema = z.object({
42
+ difficulty: DifficultySchema,
43
+ estimatedTime: z.number().int().positive(),
44
+ // seconds
45
+ bloomsTaxonomy: BloomsTaxonomySchema,
46
+ tags: z.array(z.string()),
47
+ source: z.string().default("exit-ticket"),
48
+ version: z.string().default("1.1"),
49
+ createdDate: z.string().optional(),
50
+ lastModified: z.string().optional()
51
+ });
52
+ var QuestionSchema = z.object({
53
+ // Identifiers
54
+ questionId: z.string().regex(/^[a-z0-9-]+-l\d+-q\d+$/),
55
+ globalId: z.string().regex(/^exit-ticket-\d{4}$/),
56
+ questionNumber: z.number().int().positive().max(5),
57
+ // Classification
58
+ questionType: QuestionTypeSchema,
59
+ questionTypeLabel: z.string(),
60
+ questionArchetype: QuestionArchetypeSchema.optional(),
61
+ // v1.1
62
+ // Content
63
+ prompt: z.string().min(10),
64
+ hasCodeBlock: z.boolean(),
65
+ codeLanguage: z.string().nullable(),
66
+ codeContent: z.string().nullable(),
67
+ // Misconception targeting (v1.1)
68
+ misconceptionTargets: z.array(z.string()).optional(),
69
+ // Answer options (exactly 4)
70
+ options: z.array(OptionSchema).length(4),
71
+ // Correct answer
72
+ correctAnswer: OptionKeySchema,
73
+ correctAnswerText: z.string(),
74
+ // Metadata
75
+ metadata: QuestionMetadataSchema
76
+ });
77
+ var QuestionWithoutAnswerSchema = QuestionSchema.omit({
78
+ correctAnswer: true,
79
+ correctAnswerText: true
80
+ }).extend({
81
+ options: z.array(
82
+ OptionSchema.omit({
83
+ isCorrect: true,
84
+ feedback: true,
85
+ misconceptionId: true
86
+ })
87
+ ).length(4)
88
+ });
89
+ var CourseDomainSchema = z.enum([
90
+ // AI & Data Science
91
+ "ai_data_science",
92
+ "ai_ml_cv",
93
+ "ai_generative",
94
+ "ai_advanced",
95
+ // Web Development
96
+ "web_development",
97
+ // Mobile Development
98
+ "mobile_development",
99
+ "mobile",
100
+ // Block-based Programming
101
+ "block_based",
102
+ "python_programming",
103
+ "design",
104
+ // Foundation
105
+ "foundation",
106
+ "creative_computing"
107
+ ]);
108
+ var CourseTierSchema = z.enum([
109
+ "foundation",
110
+ "intermediate",
111
+ "advanced"
112
+ ]);
113
+ var LessonSchema = z.object({
114
+ lessonId: z.string(),
115
+ // e.g., "ai-1-lesson-1"
116
+ lessonNumber: z.number().int().positive(),
117
+ lessonTitle: z.string(),
118
+ lessonSlug: z.string().optional(),
119
+ totalQuestions: z.number().int().positive().default(5),
120
+ questions: z.array(QuestionSchema)
121
+ });
122
+ var CourseSchema = z.object({
123
+ courseId: z.string(),
124
+ // e.g., "ai-1"
125
+ courseName: z.string(),
126
+ // e.g., "AI-1 Data Analysis and Data Science"
127
+ courseCode: z.string(),
128
+ // e.g., "AI1"
129
+ domain: CourseDomainSchema,
130
+ tier: CourseTierSchema,
131
+ difficulty: z.number().int().min(1).max(5),
132
+ totalLessons: z.number().int().positive(),
133
+ totalQuestions: z.number().int().positive(),
134
+ sourceFile: z.string().optional(),
135
+ lessons: z.array(LessonSchema)
136
+ });
137
+ var CourseSummarySchema = CourseSchema.omit({ lessons: true }).extend({
138
+ lessons: z.array(
139
+ LessonSchema.omit({ questions: true })
140
+ ).optional()
141
+ });
142
+ var UserResponseSchema = z.object({
143
+ questionId: z.string(),
144
+ selectedAnswer: OptionKeySchema,
145
+ timeSpent: z.number().int().nonnegative().optional(),
146
+ // milliseconds
147
+ submittedAt: z.string().datetime().optional()
148
+ });
149
+ var AssessmentSubmissionSchema = z.object({
150
+ assessmentId: z.string().uuid().optional(),
151
+ // Generated if not provided
152
+ userId: z.string().optional(),
153
+ // For authenticated users
154
+ sessionId: z.string().optional(),
155
+ // For anonymous sessions
156
+ courseId: z.string(),
157
+ lessonId: z.string().optional(),
158
+ responses: z.array(UserResponseSchema).min(1),
159
+ submittedAt: z.string().datetime(),
160
+ metadata: z.record(z.unknown()).optional()
161
+ // Custom metadata
162
+ });
163
+ var GradedResponseSchema = z.object({
164
+ questionId: z.string(),
165
+ selectedAnswer: OptionKeySchema,
166
+ correctAnswer: OptionKeySchema,
167
+ isCorrect: z.boolean(),
168
+ misconceptionId: z.string().nullable(),
169
+ // For incorrect answers
170
+ feedback: FeedbackSchema.optional(),
171
+ timeSpent: z.number().int().nonnegative().optional()
172
+ });
173
+ var MisconceptionReportSchema = z.object({
174
+ misconceptionId: z.string(),
175
+ count: z.number().int().positive(),
176
+ percentage: z.number().min(0).max(100),
177
+ questionIds: z.array(z.string()),
178
+ description: z.string().optional()
179
+ });
180
+ var GradedAssessmentSchema = z.object({
181
+ assessmentId: z.string().uuid(),
182
+ userId: z.string().optional(),
183
+ courseId: z.string(),
184
+ lessonId: z.string().optional(),
185
+ // Scores
186
+ totalQuestions: z.number().int().positive(),
187
+ correctCount: z.number().int().nonnegative(),
188
+ incorrectCount: z.number().int().nonnegative(),
189
+ score: z.number().min(0).max(100),
190
+ // Percentage
191
+ passed: z.boolean(),
192
+ // Based on threshold (default 70%)
193
+ // Detailed results
194
+ responses: z.array(GradedResponseSchema),
195
+ // Misconception analysis (v1.1 feature)
196
+ misconceptions: z.array(MisconceptionReportSchema).optional(),
197
+ topMisconceptions: z.array(z.string()).max(3).optional(),
198
+ // Timing
199
+ totalTimeSpent: z.number().int().nonnegative().optional(),
200
+ // milliseconds
201
+ averageTimePerQuestion: z.number().nonnegative().optional(),
202
+ submittedAt: z.string().datetime(),
203
+ gradedAt: z.string().datetime()
204
+ });
205
+ var ValidationResultSchema = z.object({
206
+ valid: z.boolean(),
207
+ errors: z.array(z.object({
208
+ path: z.array(z.union([z.string(), z.number()])),
209
+ message: z.string(),
210
+ code: z.string().optional()
211
+ })).optional(),
212
+ isComplete: z.boolean(),
213
+ // All questions answered
214
+ questionCount: z.number().int(),
215
+ answeredCount: z.number().int()
216
+ });
217
+
218
+ // src/index.ts
219
+ function createExitTicketsSDK(config) {
220
+ let courses = null;
221
+ const questionsById = /* @__PURE__ */ new Map();
222
+ const questionsByGlobalId = /* @__PURE__ */ new Map();
223
+ async function ensureLoaded() {
224
+ if (courses === null) {
225
+ courses = await config.dataSource.loadCourses();
226
+ for (const course of courses) {
227
+ for (const lesson of course.lessons) {
228
+ for (const question of lesson.questions) {
229
+ questionsById.set(question.questionId, question);
230
+ questionsByGlobalId.set(question.globalId, question.questionId);
231
+ }
232
+ }
233
+ }
234
+ }
235
+ return courses;
236
+ }
237
+ const data = {
238
+ async getCourses() {
239
+ return ensureLoaded();
240
+ },
241
+ async getCourse(courseId) {
242
+ const all = await ensureLoaded();
243
+ return all.find((c) => c.courseId === courseId) ?? null;
244
+ },
245
+ async getLesson(courseId, lessonNumber) {
246
+ const course = await data.getCourse(courseId);
247
+ return course?.lessons.find((l) => l.lessonNumber === lessonNumber) ?? null;
248
+ },
249
+ async getQuestion(questionId) {
250
+ await ensureLoaded();
251
+ return questionsById.get(questionId) ?? null;
252
+ },
253
+ async getAllQuestions() {
254
+ await ensureLoaded();
255
+ return Array.from(questionsById.values());
256
+ }
257
+ };
258
+ const query = {
259
+ async filterQuestions(filters) {
260
+ const all = await data.getAllQuestions();
261
+ let filtered = all;
262
+ if (filters.courseId) {
263
+ filtered = filtered.filter((q) => q.questionId.startsWith(filters.courseId));
264
+ }
265
+ if (filters.lessonNumber) {
266
+ filtered = filtered.filter((q) => q.questionId.includes(`-l${filters.lessonNumber}-`));
267
+ }
268
+ if (filters.questionType) {
269
+ filtered = filtered.filter((q) => q.questionType === filters.questionType);
270
+ }
271
+ if (filters.difficulty) {
272
+ filtered = filtered.filter((q) => q.metadata.difficulty === filters.difficulty);
273
+ }
274
+ if (filters.hasCodeBlock !== void 0) {
275
+ filtered = filtered.filter((q) => q.hasCodeBlock === filters.hasCodeBlock);
276
+ }
277
+ if (filters.tags?.length) {
278
+ filtered = filtered.filter(
279
+ (q) => filters.tags.some((tag) => q.metadata.tags.includes(tag))
280
+ );
281
+ }
282
+ const total = filtered.length;
283
+ const offset = filters.offset ?? 0;
284
+ const limit = filters.limit ?? 20;
285
+ const paginated = filtered.slice(offset, offset + limit);
286
+ return {
287
+ data: paginated,
288
+ total,
289
+ limit,
290
+ offset,
291
+ hasMore: offset + limit < total
292
+ };
293
+ },
294
+ async getRandomQuestions(count, filters) {
295
+ const result = await query.filterQuestions({ ...filters, limit: 1e3 });
296
+ const shuffled = [...result.data].sort(() => Math.random() - 0.5);
297
+ return shuffled.slice(0, count);
298
+ }
299
+ };
300
+ const quiz = {
301
+ async generateQuiz(criteria) {
302
+ const questions = await query.getRandomQuestions(criteria.questionCount, {
303
+ courseId: criteria.courseIds?.[0],
304
+ difficulty: criteria.difficulty,
305
+ questionType: criteria.questionTypes?.[0],
306
+ hasCodeBlock: criteria.includeCodeBlocks
307
+ });
308
+ return {
309
+ questions,
310
+ metadata: {
311
+ distribution: questions.reduce((acc, q) => {
312
+ acc[q.questionType] = (acc[q.questionType] ?? 0) + 1;
313
+ return acc;
314
+ }, {}),
315
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
316
+ }
317
+ };
318
+ },
319
+ async getLessonQuiz(courseId, lessonNumber) {
320
+ const lesson = await data.getLesson(courseId, lessonNumber);
321
+ return lesson?.questions ?? [];
322
+ }
323
+ };
324
+ const search = {
325
+ async search(options) {
326
+ const all = await data.getAllQuestions();
327
+ const query2 = options.query.toLowerCase();
328
+ const scored = all.map((q) => {
329
+ let score = 0;
330
+ if (q.prompt.toLowerCase().includes(query2)) score += 3;
331
+ if (q.options.some((o) => o.text.toLowerCase().includes(query2))) score += 1;
332
+ return { ...q, relevanceScore: score };
333
+ }).filter((q) => q.relevanceScore > 0).sort((a, b) => b.relevanceScore - a.relevanceScore);
334
+ const offset = options.offset ?? 0;
335
+ const limit = options.limit ?? 20;
336
+ return {
337
+ data: scored.slice(offset, offset + limit),
338
+ total: scored.length,
339
+ limit,
340
+ offset,
341
+ hasMore: offset + limit < scored.length
342
+ };
343
+ },
344
+ async searchByTags(tags, matchAll = false) {
345
+ const all = await data.getAllQuestions();
346
+ return all.filter((q) => {
347
+ if (matchAll) {
348
+ return tags.every((tag) => q.metadata.tags.includes(tag));
349
+ }
350
+ return tags.some((tag) => q.metadata.tags.includes(tag));
351
+ });
352
+ }
353
+ };
354
+ const analytics = {
355
+ async getStatistics() {
356
+ const all = await ensureLoaded();
357
+ const questions = await data.getAllQuestions();
358
+ const stats = {
359
+ totalCourses: all.length,
360
+ totalLessons: all.reduce((sum, c) => sum + c.lessons.length, 0),
361
+ totalQuestions: questions.length,
362
+ byDifficulty: {},
363
+ byQuestionType: {},
364
+ byBloomsTaxonomy: {},
365
+ withCodeBlocks: 0,
366
+ withoutCodeBlocks: 0
367
+ };
368
+ for (const q of questions) {
369
+ stats.byDifficulty[q.metadata.difficulty] = (stats.byDifficulty[q.metadata.difficulty] ?? 0) + 1;
370
+ stats.byQuestionType[q.questionType] = (stats.byQuestionType[q.questionType] ?? 0) + 1;
371
+ stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] = (stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] ?? 0) + 1;
372
+ if (q.hasCodeBlock) stats.withCodeBlocks++;
373
+ else stats.withoutCodeBlocks++;
374
+ }
375
+ return stats;
376
+ },
377
+ async getCourseStatistics(courseId) {
378
+ const course = await data.getCourse(courseId);
379
+ if (!course) throw new Error(`Course not found: ${courseId}`);
380
+ const questions = course.lessons.flatMap((l) => l.questions);
381
+ const stats = {
382
+ courseId,
383
+ totalCourses: 1,
384
+ totalLessons: course.lessons.length,
385
+ totalQuestions: questions.length,
386
+ byDifficulty: {},
387
+ byQuestionType: {},
388
+ byBloomsTaxonomy: {},
389
+ withCodeBlocks: 0,
390
+ withoutCodeBlocks: 0
391
+ };
392
+ for (const q of questions) {
393
+ stats.byDifficulty[q.metadata.difficulty] = (stats.byDifficulty[q.metadata.difficulty] ?? 0) + 1;
394
+ stats.byQuestionType[q.questionType] = (stats.byQuestionType[q.questionType] ?? 0) + 1;
395
+ stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] = (stats.byBloomsTaxonomy[q.metadata.bloomsTaxonomy] ?? 0) + 1;
396
+ if (q.hasCodeBlock) stats.withCodeBlocks++;
397
+ else stats.withoutCodeBlocks++;
398
+ }
399
+ return stats;
400
+ }
401
+ };
402
+ const validate = {
403
+ async checkAnswer(questionId, answer, mode) {
404
+ const question = await data.getQuestion(questionId);
405
+ if (!question) throw new Error(`Question not found: ${questionId}`);
406
+ const selectedOption = question.options.find((o) => o.key === answer);
407
+ if (!selectedOption) throw new Error(`Invalid answer: ${answer}`);
408
+ const isCorrect = selectedOption.isCorrect;
409
+ return {
410
+ isCorrect,
411
+ feedback: selectedOption.feedback,
412
+ misconceptionId: !isCorrect ? selectedOption.misconceptionId : null,
413
+ correctAnswer: mode === "formative" ? question.correctAnswer : void 0
414
+ };
415
+ }
416
+ };
417
+ return {
418
+ data,
419
+ query,
420
+ quiz,
421
+ search,
422
+ analytics,
423
+ validate
424
+ };
425
+ }
426
+
427
+ export { AssessmentSubmissionSchema, BloomsTaxonomySchema, CourseDomainSchema, CourseSchema, CourseSummarySchema, CourseTierSchema, DifficultySchema, FeedbackSchema, GradedAssessmentSchema, GradedResponseSchema, LessonSchema, MisconceptionReportSchema, OptionKeySchema, OptionSchema, QuestionArchetypeSchema, QuestionMetadataSchema, QuestionSchema, QuestionTypeSchema, QuestionWithoutAnswerSchema, UserResponseSchema, ValidationResultSchema, createExitTicketsSDK };
428
+ //# sourceMappingURL=index.js.map
429
+ //# sourceMappingURL=index.js.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.js","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"]}
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ // src/premium/client.ts
4
+ var PremiumClient = class {
5
+ constructor(config) {
6
+ this.token = config.token;
7
+ this.baseUrl = config.baseUrl ?? "https://api.telebort.com/exit-tickets/v1";
8
+ this.timeout = config.timeout ?? 3e4;
9
+ }
10
+ /**
11
+ * Fetch questions with full content (including answers)
12
+ */
13
+ async getQuestions(options) {
14
+ const params = new URLSearchParams();
15
+ if (options?.courseId) params.set("courseId", options.courseId);
16
+ if (options?.lessonNumber) params.set("lessonNumber", String(options.lessonNumber));
17
+ if (options?.questionType) params.set("questionType", options.questionType);
18
+ if (options?.difficulty) params.set("difficulty", options.difficulty);
19
+ if (options?.hasCodeBlock !== void 0) params.set("hasCodeBlock", String(options.hasCodeBlock));
20
+ if (options?.limit) params.set("limit", String(options.limit));
21
+ if (options?.offset) params.set("offset", String(options.offset));
22
+ const response = await this.fetch(`/questions?${params.toString()}`);
23
+ return await response.json();
24
+ }
25
+ /**
26
+ * Get a single question by ID
27
+ */
28
+ async getQuestion(questionId) {
29
+ try {
30
+ const response = await this.fetch(`/questions/${questionId}`);
31
+ const data = await response.json();
32
+ return data.question ?? null;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ /**
38
+ * Get all courses
39
+ */
40
+ async getCourses() {
41
+ const response = await this.fetch("/courses");
42
+ const data = await response.json();
43
+ return data.courses ?? [];
44
+ }
45
+ /**
46
+ * Get a single course with all questions
47
+ */
48
+ async getCourse(courseId) {
49
+ try {
50
+ const response = await this.fetch(`/courses/${courseId}`);
51
+ const data = await response.json();
52
+ return data.course ?? null;
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ /**
58
+ * Grade an assessment submission
59
+ *
60
+ * Returns detailed grading with misconception analysis
61
+ */
62
+ async gradeAssessment(assessment, options) {
63
+ const response = await this.fetch("/grade", {
64
+ method: "POST",
65
+ body: JSON.stringify({
66
+ assessment,
67
+ options: {
68
+ includeDetailedFeedback: options?.includeDetailedFeedback ?? true,
69
+ includeMisconceptionAnalysis: options?.includeMisconceptionAnalysis ?? true,
70
+ passingThreshold: options?.passingThreshold ?? 70
71
+ }
72
+ })
73
+ });
74
+ return await response.json();
75
+ }
76
+ /**
77
+ * Get overall statistics
78
+ */
79
+ async getStatistics() {
80
+ const response = await this.fetch("/analytics/statistics");
81
+ return await response.json();
82
+ }
83
+ /**
84
+ * Get course-specific statistics
85
+ */
86
+ async getCourseStatistics(courseId) {
87
+ const response = await this.fetch(`/analytics/courses/${courseId}`);
88
+ return await response.json();
89
+ }
90
+ /**
91
+ * Internal fetch wrapper with authentication
92
+ */
93
+ async fetch(path, init) {
94
+ const response = await fetch(`${this.baseUrl}${path}`, {
95
+ ...init,
96
+ headers: {
97
+ "Content-Type": "application/json",
98
+ "Authorization": `Bearer ${this.token}`,
99
+ ...init?.headers
100
+ },
101
+ signal: AbortSignal.timeout(this.timeout)
102
+ });
103
+ if (!response.ok) {
104
+ const error = await response.json().catch(() => ({
105
+ code: "SERVER_ERROR",
106
+ message: `HTTP ${response.status}: ${response.statusText}`
107
+ }));
108
+ throw new Error(`${error.code}: ${error.message}`);
109
+ }
110
+ return response;
111
+ }
112
+ };
113
+
114
+ exports.PremiumClient = PremiumClient;
115
+ //# sourceMappingURL=index.cjs.map
116
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/premium/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n Question,\n Course,\n AssessmentSubmission,\n} from '../schemas'\nimport type { Statistics } from '../types'\nimport type {\n PremiumClientConfig,\n GetQuestionsOptions,\n GradeOptions,\n GradingResult,\n PremiumApiError,\n} from './types'\n\n/**\n * Premium Tier - Full Access Client\n *\n * Provides full access to questions, grading, and analytics\n * Requires JWT authentication\n */\nexport class PremiumClient {\n private token: string\n private baseUrl: string\n private timeout: number\n\n constructor(config: PremiumClientConfig) {\n this.token = config.token\n this.baseUrl = config.baseUrl ?? 'https://api.telebort.com/exit-tickets/v1'\n this.timeout = config.timeout ?? 30000\n }\n\n /**\n * Fetch questions with full content (including answers)\n */\n async getQuestions(options?: GetQuestionsOptions): Promise<{\n questions: Question[]\n total: number\n hasMore: boolean\n }> {\n const params = new URLSearchParams()\n\n if (options?.courseId) params.set('courseId', options.courseId)\n if (options?.lessonNumber) params.set('lessonNumber', String(options.lessonNumber))\n if (options?.questionType) params.set('questionType', options.questionType)\n if (options?.difficulty) params.set('difficulty', options.difficulty)\n if (options?.hasCodeBlock !== undefined) params.set('hasCodeBlock', String(options.hasCodeBlock))\n if (options?.limit) params.set('limit', String(options.limit))\n if (options?.offset) params.set('offset', String(options.offset))\n\n const response = await this.fetch(`/questions?${params.toString()}`)\n return await response.json() as { questions: Question[]; total: number; hasMore: boolean }\n }\n\n /**\n * Get a single question by ID\n */\n async getQuestion(questionId: string): Promise<Question | null> {\n try {\n const response = await this.fetch(`/questions/${questionId}`)\n const data = await response.json() as { question?: Question }\n return data.question ?? null\n } catch {\n return null\n }\n }\n\n /**\n * Get all courses\n */\n async getCourses(): Promise<Course[]> {\n const response = await this.fetch('/courses')\n const data = await response.json() as { courses?: Course[] }\n return data.courses ?? []\n }\n\n /**\n * Get a single course with all questions\n */\n async getCourse(courseId: string): Promise<Course | null> {\n try {\n const response = await this.fetch(`/courses/${courseId}`)\n const data = await response.json() as { course?: Course }\n return data.course ?? null\n } catch {\n return null\n }\n }\n\n /**\n * Grade an assessment submission\n *\n * Returns detailed grading with misconception analysis\n */\n async gradeAssessment(\n assessment: AssessmentSubmission,\n options?: GradeOptions\n ): Promise<GradingResult> {\n const response = await this.fetch('/grade', {\n method: 'POST',\n body: JSON.stringify({\n assessment,\n options: {\n includeDetailedFeedback: options?.includeDetailedFeedback ?? true,\n includeMisconceptionAnalysis: options?.includeMisconceptionAnalysis ?? true,\n passingThreshold: options?.passingThreshold ?? 70,\n },\n }),\n })\n\n return await response.json() as GradingResult\n }\n\n /**\n * Get overall statistics\n */\n async getStatistics(): Promise<Statistics> {\n const response = await this.fetch('/analytics/statistics')\n return await response.json() as Statistics\n }\n\n /**\n * Get course-specific statistics\n */\n async getCourseStatistics(courseId: string): Promise<Statistics & { courseId: string }> {\n const response = await this.fetch(`/analytics/courses/${courseId}`)\n return await response.json() as Statistics & { courseId: string }\n }\n\n /**\n * Internal fetch wrapper with authentication\n */\n private async fetch(path: string, init?: RequestInit): Promise<Response> {\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.token}`,\n ...init?.headers,\n },\n signal: AbortSignal.timeout(this.timeout),\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({\n code: 'SERVER_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n })) as PremiumApiError\n\n throw new Error(`${error.code}: ${error.message}`)\n }\n\n return response\n }\n}\n"]}