@telebort/question-banks
v2.1.1
Published
Question Banks SDK with three-tier access: Free (schemas), Standard (validation), Premium (full access + grading)
Maintainers
Readme
@telebort/question-banks
Question Banks SDK for Telebort educational assessments. Provides Zod schemas, TypeScript types, client-side quiz generation, v2.0 Knowledge Graph access, and CAT (Computerized Adaptive Testing) orchestration.
v2.1.0 - Full CAT engine with Sympson-Hetter exposure control, IRT 4PL calibration, and 256K question bank access.
Installation
npm install @telebort/question-banks zodWhat's New in v2.1.0
CAT Orchestration Engine
- Sympson-Hetter Exposure Control: Industry-standard algorithm (used in GRE, GMAT) to prevent item overexposure
- Stopping Rules: SE-threshold + fixed-length dual criteria for test termination
- Content Balancing: Blueprint constraint satisfaction for topic coverage
- IRT 4PL: Full 4-parameter logistic model with
estimateAbilityEAP()Bayesian estimation
Scale
- 256K Questions: 26 CDN shards (~20MB each) for global delivery
- Malaysian Math: 94K bilingual questions (KSSR 30K + KSSM 22K + SPM 34K + STPM 8K)
- Knowledge Graph v2.0: 4-level taxonomy with topic-based queries
Previous Additions (v1.4.x)
- Malaysian Math Module:
sdk.malaysiaMathfor procedural question generation - 104 Templates: KSSR, KSSM, SPM Add Maths, STPM covering Years 1-6, Forms 1-6
- Bilingual: Bahasa Malaysia and English support
Architecture
The SDK provides client-side access to question bank data via CDN:
┌─────────────────────────────────────────────────────────────┐
│ SDK Access Patterns │
├─────────────────────────────────────────────────────────────┤
│ │
│ v1.x Compatibility (Course-based) │
│ └── sdk.data.getCourse('CS1') │
│ │
│ v2.0 Knowledge Graph (Topic-based) │
│ ├── sdk.knowledge.getByTopic('algorithms/sorting') │
│ └── sdk.taxonomy.getDomains() │
│ │
│ Premium API (JWT-authenticated) │
│ └── PremiumClient with offline fallback │
│ │
└─────────────────────────────────────────────────────────────┘No server required - all operations work client-side.
Quick Start (CDN Mode)
Fetch questions from CDN for automatic updates:
import { createQuestionBanksSDK } from '@telebort/question-banks'
const CDN_BASE = 'https://content-warehouse.vercel.app/question-banks/v1.1/courses'
const sdk = createQuestionBanksSDK({
dataSource: {
async loadCourses() {
const codes = ['AI1', 'M1', 'W1', 'CS1']
const courses = await Promise.all(
codes.map(code => fetch(`${CDN_BASE}/${code}.json`).then(r => r.json()))
)
return courses
}
}
})
// Generate a quiz
const quiz = await sdk.quiz.generateQuiz({ questionCount: 5 })
// Check an answer with feedback
const result = await sdk.validate.checkAnswer('ai-1-l1-q1', 'B', 'formative')
// { isCorrect: true, feedback: {...}, misconceptionId: null }v2.0 Knowledge Graph
Access questions by topic instead of course. Query across curricula with the 4-level taxonomy.
Full Guide: See KNOWLEDGE-GRAPH.md for complete API reference and examples.
Taxonomy Structure
Domain (L1) Strand (L2) Topic (L3)
│ │ │
computer-science ──→ algorithms ──────→ sorting
data-structures ──→ arrays, trees, graphs
web-development ──→ javascript ───────→ arrow-functions, promises
html-css ─────────→ flexbox, grid6 Domains: computer-science, artificial-intelligence, web-development, mathematics, computational-thinking, mobile-development
Basic Usage
import { createKnowledgeModule, createTaxonomyModule } from '@telebort/question-banks'
const CDN_BASE = 'https://content-warehouse.vercel.app'
// Create modules
const knowledge = createKnowledgeModule({ cdnBase: CDN_BASE })
const taxonomy = createTaxonomyModule({ cdnBase: CDN_BASE })
// Get questions by topic path
const sorting = await knowledge.getByTopic('computer-science/algorithms/sorting')
console.log(`${sorting.length} sorting questions`) // Returns TopicQuestionRef[]
// Browse taxonomy
const domains = await taxonomy.getDomains()
const strands = await taxonomy.getStrands('computer-science')Advanced Search with Filters
// Find medium-difficulty sorting questions from CS1
const results = await knowledge.search({
domain: 'computer-science',
strand: 'algorithms',
topic: 'sorting',
difficulty: 'medium',
courseCode: 'CS1',
limit: 10,
offset: 0
})
// Paginate through all web-dev questions
let page = await knowledge.search({ domain: 'web-development', limit: 50, offset: 0 })Error Handling with Debug Logging
const knowledge = createKnowledgeModule({
cdnBase: CDN_BASE,
cacheEnabled: true, // Enable caching
debug: true, // Log to console
onError: (error, context) => {
console.error(`[${context}]`, error.message)
// Send to monitoring: Sentry.captureException(error)
}
})v2.0 CDN Structure (256K Questions)
/question-banks/v2.0/
├── courses/{CODE}.json # Course-based (v1.x compatible)
├── by-topic/{domain}/{strand}/{topic}.json # Topic-based indexes
├── taxonomy/topics.json # 4-level topic tree
└── master/
├── knowledge-bank-index.json # Shard metadata (50KB)
└── shards/shard-{00001-00026}.json # 10K questions each (~20MB)Total: 256,037 questions in 26 CDN shards for global delivery.
CDN Endpoints
Base URL: https://content-warehouse.vercel.app
| Endpoint | Size | Content |
|----------|------|---------|
| /question-banks/v1.1/courses/AI1.json | ~400KB | Single course |
| /question-banks/v1.1/courses/M1.json | ~400KB | Single course |
| /certification/v1.1/courses/AI1.json | ~2KB | Certification metadata |
Available Courses: AI1, AI2, AI25, AI3, M1, M2, M3, W1, W2, W3, W35, W4, F1, F2, JC, BBW, BBP, BBD, BBAI, CS1, CS2, CS3, CS4
Static Mode (Bundled JSON)
For offline-first apps, bundle the JSON with your build:
import { createQuestionBanksSDK } from '@telebort/question-banks'
import coursesData from './data/courses.json'
const sdk = createQuestionBanksSDK({
dataSource: {
async loadCourses() {
return coursesData.courses
}
}
})
// All SDK features work offline
const courses = await sdk.data.getCourses()
const quiz = await sdk.quiz.generateQuiz({ questionCount: 5 })
const result = await sdk.validate.checkAnswer('ai-1-l1-q1', 'B', 'formative')Premium Client (JWT-Authenticated)
For full API access with offline fallback (v1.3.0+):
import { PremiumClient } from '@telebort/question-banks/premium'
const client = new PremiumClient({
token: 'your-jwt-token',
// Offline support (v1.3.0)
offlineFallback: true, // Use cache when network fails
cacheTTL: 300000, // 5 minutes cache TTL
// Rate limiting (built-in: 10 req/s, burst 20)
timeout: 30000, // Request timeout
// Debug logging
logger: (msg, level) => console.log(`[${level}] ${msg}`),
})
// Preload courses for offline use
await client.preloadForOffline(['cs-1', 'cs-2', 'ai-1'])
// Fetch questions (uses cache if offline)
const { questions, total, hasMore } = await client.getQuestions({
courseId: 'cs-1',
difficulty: 'medium',
limit: 10,
})
// Grade an assessment
const result = await client.gradeAssessment({
assessmentId: 'uuid-here',
courseId: 'cs-1',
submittedAt: new Date().toISOString(),
responses: [
{ questionId: 'q1', selectedAnswer: 'A' },
{ questionId: 'q2', selectedAnswer: 'B' },
]
})
// Cache management
const stats = client.getCacheStats()
console.log(`Cached entries: ${stats.entries}`)
client.clearCache()SDK Interface
interface QuestionBanksSDK {
// v1.x Course-based access
data: {
getCourses(): Promise<Course[]>
getCourse(courseId: string): Promise<Course | null>
getLesson(courseId: string, lessonNumber: number): Promise<Lesson | null>
getQuestion(questionId: string): Promise<Question | null>
getAllQuestions(): Promise<Question[]>
}
query: {
filterQuestions(filters: QuestionFilters): Promise<QueryResult<Question>>
getRandomQuestions(count: number, filters?: QuestionFilters): Promise<Question[]>
}
quiz: {
generateQuiz(criteria: QuizCriteria): Promise<{ questions: Question[]; metadata: object }>
getLessonQuiz(courseId: string, lessonNumber: number): Promise<Question[]>
}
search: {
search(options: SearchOptions): Promise<QueryResult<Question>>
searchByTags(tags: string[], matchAll?: boolean): Promise<Question[]>
}
analytics: {
getStatistics(): Promise<Statistics>
getCourseStatistics(courseId: string): Promise<Statistics>
}
validate: {
checkAnswer(questionId: string, answer: string, mode: 'formative' | 'summative'): Promise<AnswerResult>
}
// v2.0 Topic-based access (NEW)
knowledge: {
getByTopic(topicPath: string): Promise<TopicIndex>
getByDomain(domainId: string): Promise<Question[]>
search(filters: KnowledgeFilters): Promise<Question[]>
}
taxonomy: {
getDomains(): Promise<string[]>
getTopics(): Promise<TaxonomyTree>
}
}Zod Schemas (Free Validation)
Import Zod schemas for offline validation:
import { QuestionSchema, CourseSchema } from '@telebort/question-banks/schemas'
import { z } from 'zod'
// Validate a question
const result = QuestionSchema.safeParse(myQuestion)
if (!result.success) {
console.error(result.error.issues)
}
// Type inference
type Question = z.infer<typeof QuestionSchema>Available Schemas
// Question schemas
QuestionSchema // Full question with v1.1 enhancements
OptionSchema // Answer option with misconception tracking
FeedbackSchema // Structured feedback (short/detailed/socraticHint)
// Course schemas
CourseSchema // Course with lessons and questions
LessonSchema // Lesson with questions
// Assessment schemas
AssessmentSubmissionSchema // User assessment submission
GradedAssessmentSchema // Graded result with misconception analysisCLI Tools
The SDK includes command-line tools for working with question bank data:
# Install globally
npm install -g @telebort/question-banks
# Or use via npx
npx @telebort/question-banks <command>Commands
validate - Validate Course JSON Files
# Validate a single file
question-banks validate ./data/courses/ai-1.json
# Validate all files in a directory
question-banks validate ./data/courses/
# Show all validation errors
question-banks validate ./data/courses/ --verbosestats - Show Statistics
# Show aggregate statistics
question-banks stats --data-dir ./data/courses/
# Show statistics for a specific course
question-banks stats --course ai-1 --data-dir ./data/courses/quiz - Generate Sample Quizzes
# Generate 5 random questions
question-banks quiz --data-dir ./data/courses/
# Generate 10 questions from a specific course
question-banks quiz --course ai-1 --count 10 --data-dir ./data/courses/
# Filter by difficulty and show answers
question-banks quiz --difficulty hard --show-answers --data-dir ./data/courses/Question Schema (v1.1)
Questions include v1.1 enhancements for misconception tracking:
interface Question {
questionId: string // "ai-1-l1-q1"
globalId: string // "exit-ticket-0351"
questionType: QuestionType // vocabulary, code_understanding, etc.
questionArchetype?: string // vocabulary, trace, bebras, blockmodel
prompt: string
hasCodeBlock: boolean
codeLanguage: string | null
codeContent: string | null
// v1.1: Misconception targeting
misconceptionTargets?: string[]
options: Option[] // 4 options with misconception feedback
correctAnswer: 'A' | 'B' | 'C' | 'D'
metadata: {
difficulty: 'easy' | 'medium' | 'hard' | 'challenge'
bloomsTaxonomy: 'remember' | 'understand' | 'apply' | ...
tags: string[]
}
}
interface Option {
key: 'A' | 'B' | 'C' | 'D'
text: string
isCorrect: boolean
// v1.1: Misconception feedback
misconceptionId?: string // "INPUT_PROMPT_VS_OUTPUT"
feedback?: {
short: string // "Not quite! That's the prompt."
detailed: string // Full explanation
socraticHint?: string // "Where does the program display its result?"
}
}TypeScript Support
All types are inferred from Zod schemas:
import type {
Question,
Course,
AssessmentSubmission,
GradedAssessment
} from '@telebort/question-banks'License
MIT
Support
- Issues: GitHub Issues
