@contractspec/module.learning-journey 4.0.0 → 4.0.2

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.
@@ -78,4 +78,4 @@ const updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });
78
78
  ## Verification
79
79
  - Add fixtures for streak/XP rule changes and quiz scoring.
80
80
  - Ensure Notifications/Audit wiring persists for completions; analytics emitted for progress.
81
- - Use Feature Flags to trial new tracks or reward rules; default safe/off.`}];Bt(jt);var it={learningSteps:[1,10],graduatingInterval:1,easyInterval:4,relearningSteps:[10],minEaseFactor:1.3,maxInterval:365,intervalModifier:1,newIntervalModifier:0.5,hardIntervalModifier:1.2,easyBonus:1.3};class Dr{config;constructor(r={}){this.config={...it,...r}}calculateNextReview(r,t,o=new Date){if(!r.isGraduated&&!r.isRelearning)return this.handleLearningCard(r,t,o);if(r.isRelearning)return this.handleRelearningCard(r,t,o);return this.handleReviewCard(r,t,o)}getInitialState(){return{interval:0,easeFactor:2.5,repetitions:0,learningStep:0,isGraduated:!1,isRelearning:!1,lapses:0}}isDue(r,t=new Date){return r<=t}getOverdueDays(r,t=new Date){let o=t.getTime()-r.getTime();return Math.floor(o/86400000)}handleLearningCard(r,t,o){let s=this.config.learningSteps,c=r.learningStep,A=!1,I=0,O;switch(t){case"AGAIN":c=0,I=s[0]??1,O=this.addMinutes(o,I);break;case"HARD":I=s[c]??s[0]??1,O=this.addMinutes(o,I);break;case"GOOD":if(c++,c>=s.length)A=!0,I=this.config.graduatingInterval,O=this.addDays(o,I);else I=s[c]??10,O=this.addMinutes(o,I);break;case"EASY":A=!0,I=this.config.easyInterval,O=this.addDays(o,I);break}return{interval:A?I:0,easeFactor:r.easeFactor,repetitions:A?1:0,nextReviewAt:O,learningStep:c,isGraduated:A,isRelearning:!1,lapses:r.lapses}}handleRelearningCard(r,t,o){let s=this.config.relearningSteps,c=r.learningStep,A=!0,I=0,O;switch(t){case"AGAIN":c=0,I=s[0]??10,O=this.addMinutes(o,I);break;case"HARD":I=s[c]??s[0]??10,O=this.addMinutes(o,I);break;case"GOOD":if(c++,c>=s.length)A=!1,I=Math.max(1,Math.floor(r.interval*this.config.newIntervalModifier)),O=this.addDays(o,I);else I=s[c]??10,O=this.addMinutes(o,I);break;case"EASY":A=!1,I=Math.max(1,Math.floor(r.interval*this.config.newIntervalModifier*1.5)),O=this.addDays(o,I);break}return{interval:A?r.interval:I,easeFactor:r.easeFactor,repetitions:A?r.repetitions:r.repetitions+1,nextReviewAt:O,learningStep:c,isGraduated:!0,isRelearning:A,lapses:r.lapses}}handleReviewCard(r,t,o){let s,c=r.easeFactor,A=r.repetitions,I=!1,O=0,j=r.lapses;switch(t){case"AGAIN":return j++,I=!0,O=0,c=Math.max(this.config.minEaseFactor,c-0.2),s=r.interval,{interval:s,easeFactor:c,repetitions:A,nextReviewAt:this.addMinutes(o,this.config.relearningSteps[0]??10),learningStep:O,isGraduated:!0,isRelearning:!0,lapses:j};case"HARD":c=Math.max(this.config.minEaseFactor,c-0.15),s=Math.max(r.interval+1,r.interval*this.config.hardIntervalModifier);break;case"GOOD":s=r.interval*c*this.config.intervalModifier,A++;break;case"EASY":c=c+0.15,s=r.interval*c*this.config.easyBonus*this.config.intervalModifier,A++;break}return s=Math.min(Math.round(s),this.config.maxInterval),s=Math.max(1,s),{interval:s,easeFactor:c,repetitions:A,nextReviewAt:this.addDays(o,s),learningStep:O,isGraduated:!0,isRelearning:I,lapses:j}}addMinutes(r,t){return new Date(r.getTime()+t*60*1000)}addDays(r,t){return new Date(r.getTime()+t*24*60*60*1000)}}var Vo=new Dr;var Kt={timezone:"UTC",freezesPerMonth:2,maxFreezes:5,gracePeriodHours:4};class U{config;constructor(r={}){this.config={...Kt,...r}}update(r,t=new Date){let o=this.getDateString(t),s={state:{...r},streakMaintained:!1,streakLost:!1,freezeUsed:!1,newStreak:!1,daysMissed:0};if(!r.lastActivityDate)return s.state.currentStreak=1,s.state.longestStreak=Math.max(1,r.longestStreak),s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.newStreak=!0,s.streakMaintained=!0,s;if(r.lastActivityDate===o)return s.state.lastActivityAt=t,s.streakMaintained=!0,s;let c=this.getDaysBetween(r.lastActivityDate,o);if(c===1)return s.state.currentStreak=r.currentStreak+1,s.state.longestStreak=Math.max(s.state.currentStreak,r.longestStreak),s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.streakMaintained=!0,s;s.daysMissed=c-1;let A=s.daysMissed;if(A<=r.freezesRemaining)return s.state.freezesRemaining=r.freezesRemaining-A,s.state.freezeUsedAt=t,s.state.currentStreak=r.currentStreak+1,s.state.longestStreak=Math.max(s.state.currentStreak,r.longestStreak),s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.freezeUsed=!0,s.streakMaintained=!0,s;return s.streakLost=!0,s.state.currentStreak=1,s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.newStreak=!0,s}checkStatus(r,t=new Date){if(!r.lastActivityDate)return{isActive:!1,willExpireAt:null,canUseFreeze:!1,daysUntilExpiry:0};let o=this.getDateString(t),s=this.getDaysBetween(r.lastActivityDate,o);if(s===0){let A=this.addDays(t,1);return A.setHours(23,59,59,999),{isActive:!0,willExpireAt:A,canUseFreeze:r.freezesRemaining>0,daysUntilExpiry:1}}if(s===1){let A=new Date(t);return A.setHours(23+this.config.gracePeriodHours,59,59,999),{isActive:!0,willExpireAt:A,canUseFreeze:r.freezesRemaining>0,daysUntilExpiry:0}}let c=s-1;return{isActive:c<=r.freezesRemaining,willExpireAt:null,canUseFreeze:c<=r.freezesRemaining,daysUntilExpiry:-c}}useFreeze(r,t=new Date){if(r.freezesRemaining<=0)return null;return{...r,freezesRemaining:r.freezesRemaining-1,freezeUsedAt:t}}awardMonthlyFreezes(r){return{...r,freezesRemaining:Math.min(r.freezesRemaining+this.config.freezesPerMonth,this.config.maxFreezes)}}getInitialState(){return{currentStreak:0,longestStreak:0,lastActivityAt:null,lastActivityDate:null,freezesRemaining:this.config.freezesPerMonth,freezeUsedAt:null}}getMilestones(r){let t=[3,7,14,30,60,90,180,365,500,1000],o=t.filter((c)=>r>=c),s=t.find((c)=>r<c)??null;return{achieved:o,next:s}}getDateString(r){let t=r.getFullYear(),o=String(r.getMonth()+1).padStart(2,"0"),s=String(r.getDate()).padStart(2,"0");return`${t}-${o}-${s}`}getDaysBetween(r,t){let o=new Date(r),c=new Date(t).getTime()-o.getTime();return Math.floor(c/86400000)}addDays(r,t){return new Date(r.getTime()+t*24*60*60*1000)}}var Zo=new U;import{defineTranslation as Ct}from"@contractspec/lib.contracts-spec/translations";var Br=Ct({meta:{key:"learning-journey.messages",version:"1.0.0",domain:"learning-journey",description:"XP source labels for the learning-journey module",owners:["platform"],stability:"experimental"},locale:"en",fallback:"en",messages:{"xp.source.base":{value:"Base",description:"XP breakdown label for base XP"},"xp.source.scoreBonus":{value:"Score Bonus",description:"XP breakdown label for score-based bonus"},"xp.source.perfectScore":{value:"Perfect Score",description:"XP breakdown label for perfect score bonus"},"xp.source.firstAttempt":{value:"First Attempt",description:"XP breakdown label for first attempt bonus"},"xp.source.retryPenalty":{value:"Retry Penalty",description:"XP breakdown label for retry penalty"},"xp.source.streakBonus":{value:"Streak Bonus",description:"XP breakdown label for streak bonus"}}});import{defineTranslation as yt}from"@contractspec/lib.contracts-spec/translations";var jr=yt({meta:{key:"learning-journey.messages",version:"1.0.0",domain:"learning-journey",description:"XP source labels (Spanish)",owners:["platform"],stability:"experimental"},locale:"es",fallback:"en",messages:{"xp.source.base":{value:"Base",description:"XP breakdown label for base XP"},"xp.source.scoreBonus":{value:"Bonificación por puntuación",description:"XP breakdown label for score-based bonus"},"xp.source.perfectScore":{value:"Puntuación perfecta",description:"XP breakdown label for perfect score bonus"},"xp.source.firstAttempt":{value:"Primer intento",description:"XP breakdown label for first attempt bonus"},"xp.source.retryPenalty":{value:"Penalización por reintento",description:"XP breakdown label for retry penalty"},"xp.source.streakBonus":{value:"Bonificación por racha",description:"XP breakdown label for streak bonus"}}});import{defineTranslation as _t}from"@contractspec/lib.contracts-spec/translations";var ir=_t({meta:{key:"learning-journey.messages",version:"1.0.0",domain:"learning-journey",description:"XP source labels (French)",owners:["platform"],stability:"experimental"},locale:"fr",fallback:"en",messages:{"xp.source.base":{value:"Base",description:"XP breakdown label for base XP"},"xp.source.scoreBonus":{value:"Bonus de score",description:"XP breakdown label for score-based bonus"},"xp.source.perfectScore":{value:"Score parfait",description:"XP breakdown label for perfect score bonus"},"xp.source.firstAttempt":{value:"Premier essai",description:"XP breakdown label for first attempt bonus"},"xp.source.retryPenalty":{value:"Pénalité de réessai",description:"XP breakdown label for retry penalty"},"xp.source.streakBonus":{value:"Bonus de série",description:"XP breakdown label for streak bonus"}}});import{createI18nFactory as qt}from"@contractspec/lib.contracts-spec/translations";var tr=qt({specKey:"learning-journey.messages",catalogs:[Br,ir,jr]}),Kr=tr.create,Yo=tr.getDefault,Go=tr.resetRegistry;var w={baseValues:{lesson_complete:10,quiz_pass:20,quiz_perfect:50,flashcard_review:1,course_complete:200,module_complete:50,streak_bonus:5,achievement_unlock:0,daily_goal_complete:15,first_lesson:25,journey_step:5,journey_complete:50},scoreThresholds:[{min:90,multiplier:1.5},{min:80,multiplier:1.25},{min:70,multiplier:1},{min:60,multiplier:0.75},{min:0,multiplier:0.5}],streakTiers:[{days:365,bonus:50},{days:180,bonus:30},{days:90,bonus:20},{days:30,bonus:15},{days:14,bonus:10},{days:7,bonus:5},{days:3,bonus:2},{days:1,bonus:0}],perfectScoreMultiplier:1.5,firstAttemptBonus:10,retryPenalty:0.5,speedBonusMultiplier:1.2,speedBonusThreshold:0.8};class v{config;constructor(r={}){this.config={...w,...r,baseValues:{...w.baseValues,...r.baseValues},scoreThresholds:r.scoreThresholds||w.scoreThresholds,streakTiers:r.streakTiers||w.streakTiers}}calculate(r){let t=[],o=r.baseXp??this.config.baseValues[r.activity],s=o;if(t.push({source:"base",amount:o}),r.score!==void 0){let c=this.getScoreMultiplier(r.score);if(c!==1){let A=Math.round(o*(c-1));s+=A,t.push({source:"score_bonus",amount:A,multiplier:c})}if(r.score===100){let A=Math.round(o*(this.config.perfectScoreMultiplier-1));s+=A,t.push({source:"perfect_score",amount:A,multiplier:this.config.perfectScoreMultiplier})}}if(r.attemptNumber===1&&!r.isRetry)s+=this.config.firstAttemptBonus,t.push({source:"first_attempt",amount:this.config.firstAttemptBonus});if(r.isRetry){let c=Math.round(s*(1-this.config.retryPenalty));s-=c,t.push({source:"retry_penalty",amount:-c,multiplier:this.config.retryPenalty})}if(r.currentStreak&&r.currentStreak>0){let c=this.getStreakBonus(r.currentStreak);if(c>0)s+=c,t.push({source:"streak_bonus",amount:c})}if(o>0)s=Math.max(1,s);return{totalXp:Math.round(s),baseXp:o,breakdown:t}}calculateStreakBonus(r){let t=this.getStreakBonus(r);return{totalXp:t,baseXp:t,breakdown:[{source:"streak_bonus",amount:t}]}}getXpForLevel(r){if(r<=1)return 0;return Math.round(100*Math.pow(r-1,1.5))}getLevelFromXp(r){let t=1,o=this.getXpForLevel(t+1);while(r>=o&&t<1000)t++,o=this.getXpForLevel(t+1);let s=this.getXpForLevel(t),c=this.getXpForLevel(t+1);return{level:t,xpInLevel:r-s,xpForNextLevel:c-s}}getScoreMultiplier(r){for(let t of this.config.scoreThresholds)if(r>=t.min)return t.multiplier;return 1}getStreakBonus(r){for(let t of this.config.streakTiers)if(r>=t.days)return t.bonus;return 0}}var Ht={base:"xp.source.base",score_bonus:"xp.source.scoreBonus",perfect_score:"xp.source.perfectScore",first_attempt:"xp.source.firstAttempt",retry_penalty:"xp.source.retryPenalty",streak_bonus:"xp.source.streakBonus"};function No(r,t){let o=Kr(t),s=Ht[r];return s?o.t(s):r}var To=new v;import{defineEntity as z,defineEntityEnum as Cr,field as e,index as H}from"@contractspec/lib.schema";var yr=Cr({name:"LearningStyle",values:["VISUAL","AUDITORY","READING","KINESTHETIC","MIXED"],schema:"lssm_learning",description:"Preferred learning style."}),_r=Cr({name:"RecommendationType",values:["COURSE","LESSON","REVIEW","PRACTICE","ASSESSMENT","DECK"],schema:"lssm_learning",description:"Type of learning recommendation."}),xt=z({name:"LearnerProfile",description:"AI personalization profile for a learner.",schema:"lssm_learning",map:"learner_profile",fields:{id:e.id({description:"Unique profile identifier"}),learnerId:e.foreignKey({description:"Learner"}),learningStyle:e.enum("LearningStyle",{default:"MIXED",description:"Preferred learning style"}),preferredDifficulty:e.string({default:'"adaptive"',description:"Difficulty preference"}),preferredSessionLength:e.int({default:30,description:"Preferred session length in minutes"}),interests:e.json({isOptional:!0,description:"Topic interests"}),goals:e.json({isOptional:!0,description:"Learning goals"}),pacePreference:e.string({default:'"normal"',description:"Learning pace: slow, normal, fast"}),bestTimeOfDay:e.string({isOptional:!0,description:"Best time for learning"}),averageSessionLength:e.int({isOptional:!0,description:"Average session length"}),daysActivePerWeek:e.int({isOptional:!0,description:"Days active per week"}),avgQuizScore:e.int({isOptional:!0,description:"Average quiz score"}),avgLessonCompletionTime:e.int({isOptional:!0,description:"Avg lesson completion time"}),strengths:e.json({isOptional:!0,description:"Identified strengths"}),weaknesses:e.json({isOptional:!0,description:"Areas for improvement"}),lastAnalyzedAt:e.dateTime({isOptional:!0,description:"Last AI analysis"}),metadata:e.json({isOptional:!0,description:"Additional metadata"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.unique(["learnerId"],{name:"learner_profile_unique"}),H.on(["learningStyle"])],enums:[yr]}),Vt=z({name:"SkillMap",description:"Maps learner proficiency across skills.",schema:"lssm_learning",map:"skill_map",fields:{id:e.id({description:"Unique skill map identifier"}),learnerId:e.foreignKey({description:"Learner"}),skillId:e.string({description:"Skill identifier"}),skillName:e.string({description:"Skill name"}),skillCategory:e.string({isOptional:!0,description:"Skill category"}),level:e.int({default:0,description:"Proficiency level (0-100)"}),confidence:e.decimal({default:0.5,description:"Confidence in assessment"}),lessonsCompleted:e.int({default:0,description:"Related lessons completed"}),quizzesCompleted:e.int({default:0,description:"Related quizzes completed"}),practiceTime:e.int({default:0,description:"Practice time in minutes"}),lastPracticedAt:e.dateTime({isOptional:!0,description:"Last practice time"}),learningVelocity:e.decimal({isOptional:!0,description:"Learning speed for this skill"}),predictedTimeToMastery:e.int({isOptional:!0,description:"Predicted time to mastery (minutes)"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.unique(["learnerId","skillId"],{name:"skill_map_unique"}),H.on(["skillId","level"]),H.on(["learnerId","level"])]}),Lt=z({name:"LearningPath",description:"AI-generated personalized learning path.",schema:"lssm_learning",map:"learning_path",fields:{id:e.id({description:"Unique path identifier"}),learnerId:e.foreignKey({description:"Learner"}),name:e.string({description:"Path name"}),description:e.string({isOptional:!0,description:"Path description"}),goal:e.string({isOptional:!0,description:"Path goal"}),steps:e.json({description:"Ordered list of learning steps"}),currentStepIndex:e.int({default:0,description:"Current step index"}),progress:e.int({default:0,description:"Completion percentage"}),completedSteps:e.int({default:0,description:"Steps completed"}),totalSteps:e.int({default:0,description:"Total steps"}),generatedAt:e.dateTime({description:"When path was generated"}),adaptedFrom:e.string({isOptional:!0,description:"Original path ID if adapted"}),generationParams:e.json({isOptional:!0,description:"AI generation parameters"}),adaptationHistory:e.json({isOptional:!0,description:"Path adaptation history"}),isActive:e.boolean({default:!0,description:"Whether path is active"}),isCompleted:e.boolean({default:!1,description:"Whether path is completed"}),startedAt:e.dateTime({isOptional:!0,description:"When started"}),completedAt:e.dateTime({isOptional:!0,description:"When completed"}),estimatedCompletionDate:e.dateTime({isOptional:!0,description:"Estimated completion"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.on(["learnerId","isActive"]),H.on(["generatedAt"])]}),Zt=z({name:"Recommendation",description:"AI-powered learning recommendation.",schema:"lssm_learning",map:"recommendation",fields:{id:e.id({description:"Unique recommendation identifier"}),learnerId:e.foreignKey({description:"Learner"}),type:e.enum("RecommendationType",{description:"Recommendation type"}),itemId:e.string({description:"Recommended item ID"}),itemType:e.string({description:"Item type (course, lesson, deck, etc.)"}),score:e.decimal({description:"Recommendation score (0-1)"}),confidence:e.decimal({description:"Confidence in recommendation"}),reason:e.string({description:"Human-readable reason"}),factors:e.json({isOptional:!0,description:"Factors that contributed to recommendation"}),status:e.string({default:'"pending"',description:"Status: pending, viewed, accepted, dismissed"}),viewedAt:e.dateTime({isOptional:!0,description:"When viewed"}),acceptedAt:e.dateTime({isOptional:!0,description:"When accepted"}),dismissedAt:e.dateTime({isOptional:!0,description:"When dismissed"}),feedback:e.string({isOptional:!0,description:"User feedback"}),feedbackRating:e.int({isOptional:!0,description:"Feedback rating (1-5)"}),expiresAt:e.dateTime({isOptional:!0,description:"When recommendation expires"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.on(["learnerId","status","score"]),H.on(["type","status"]),H.on(["expiresAt"])],enums:[_r]}),$t=z({name:"LearningGap",description:"Identified learning gap.",schema:"lssm_learning",map:"learning_gap",fields:{id:e.id({description:"Unique gap identifier"}),learnerId:e.foreignKey({description:"Learner"}),skillId:e.string({description:"Skill with gap"}),skillName:e.string({description:"Skill name"}),severity:e.string({default:'"moderate"',description:"Gap severity: minor, moderate, major"}),confidence:e.decimal({description:"Confidence in gap detection"}),evidence:e.json({isOptional:!0,description:"Evidence for gap"}),relatedQuestions:e.json({isOptional:!0,description:"Questions that revealed gap"}),suggestedRemediation:e.json({isOptional:!0,description:"Suggested remediation"}),remediationProgress:e.int({default:0,description:"Remediation progress"}),status:e.string({default:'"open"',description:"Status: open, in_progress, resolved"}),resolvedAt:e.dateTime({isOptional:!0,description:"When resolved"}),detectedAt:e.dateTime({description:"When gap was detected"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.on(["learnerId","status"]),H.on(["skillId","status"]),H.on(["severity","status"])]}),qr=[xt,Vt,Lt,Zt,$t],Hr=[yr,_r];import{defineEntity as Y,defineEntityEnum as G,field as b,index as Z}from"@contractspec/lib.schema";var xr=G({name:"CourseDifficulty",values:["BEGINNER","INTERMEDIATE","ADVANCED","EXPERT"],schema:"lssm_learning",description:"Difficulty level of a course."}),Vr=G({name:"CourseStatus",values:["DRAFT","PUBLISHED","ARCHIVED"],schema:"lssm_learning",description:"Publication status of a course."}),Lr=G({name:"LessonType",values:["CONTENT","VIDEO","INTERACTIVE","QUIZ","PRACTICE","PROJECT"],schema:"lssm_learning",description:"Type of lesson content."}),Zr=G({name:"ContentType",values:["MARKDOWN","VIDEO","AUDIO","EMBED","SCORM","CUSTOM"],schema:"lssm_learning",description:"Type of lesson content format."}),Mt=Y({name:"Course",description:"A structured learning course.",schema:"lssm_learning",map:"course",fields:{id:b.id({description:"Unique course identifier"}),title:b.string({description:"Course title"}),slug:b.string({isUnique:!0,description:"URL-friendly slug"}),description:b.string({isOptional:!0,description:"Course description"}),summary:b.string({isOptional:!0,description:"Short summary"}),difficulty:b.enum("CourseDifficulty",{default:"BEGINNER",description:"Difficulty level"}),category:b.string({isOptional:!0,description:"Course category"}),tags:b.json({isOptional:!0,description:"Tags for discovery"}),prerequisites:b.json({isOptional:!0,description:"Required course IDs"}),requiredSkills:b.json({isOptional:!0,description:"Required skill levels"}),estimatedDuration:b.int({isOptional:!0,description:"Estimated duration in minutes"}),thumbnailUrl:b.string({isOptional:!0,description:"Thumbnail image URL"}),coverImageUrl:b.string({isOptional:!0,description:"Cover image URL"}),promoVideoUrl:b.string({isOptional:!0,description:"Promo video URL"}),status:b.enum("CourseStatus",{default:"DRAFT",description:"Publication status"}),publishedAt:b.dateTime({isOptional:!0,description:"When published"}),authorId:b.string({description:"Author user ID"}),orgId:b.string({isOptional:!0,description:"Organization scope"}),isPublic:b.boolean({default:!1,description:"Whether course is publicly accessible"}),isFeatured:b.boolean({default:!1,description:"Whether course is featured"}),certificateEnabled:b.boolean({default:!1,description:"Award certificate on completion"}),metadata:b.json({isOptional:!0,description:"Additional metadata"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),modules:b.hasMany("CourseModule"),enrollments:b.hasMany("Enrollment")},indexes:[Z.on(["orgId","status"]),Z.on(["category"]),Z.on(["difficulty"]),Z.on(["authorId"])],enums:[xr,Vr]}),Wt=Y({name:"CourseModule",description:"A module (section) within a course.",schema:"lssm_learning",map:"course_module",fields:{id:b.id({description:"Unique module identifier"}),courseId:b.foreignKey({description:"Parent course"}),title:b.string({description:"Module title"}),description:b.string({isOptional:!0,description:"Module description"}),order:b.int({default:0,description:"Display order"}),unlockCondition:b.json({isOptional:!0,description:"Conditions to unlock module"}),prerequisiteModuleIds:b.json({isOptional:!0,description:"Required modules to complete first"}),estimatedDuration:b.int({isOptional:!0,description:"Estimated duration in minutes"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),course:b.belongsTo("Course",["courseId"],["id"],{onDelete:"Cascade"}),lessons:b.hasMany("Lesson"),completions:b.hasMany("ModuleCompletion")},indexes:[Z.on(["courseId","order"])]}),mt=Y({name:"Lesson",description:"An individual lesson within a module.",schema:"lssm_learning",map:"lesson",fields:{id:b.id({description:"Unique lesson identifier"}),moduleId:b.foreignKey({description:"Parent module"}),title:b.string({description:"Lesson title"}),description:b.string({isOptional:!0,description:"Lesson description"}),type:b.enum("LessonType",{default:"CONTENT",description:"Lesson type"}),order:b.int({default:0,description:"Display order"}),estimatedDuration:b.int({isOptional:!0,description:"Estimated duration in minutes"}),xpReward:b.int({default:10,description:"XP awarded on completion"}),isFree:b.boolean({default:!1,description:"Whether lesson is free preview"}),isRequired:b.boolean({default:!0,description:"Whether lesson is required for completion"}),metadata:b.json({isOptional:!0,description:"Additional metadata"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),module:b.belongsTo("CourseModule",["moduleId"],["id"],{onDelete:"Cascade"}),contents:b.hasMany("LessonContent"),progress:b.hasMany("LessonProgress"),quizzes:b.hasMany("Quiz")},indexes:[Z.on(["moduleId","order"]),Z.on(["type"])],enums:[Lr]}),Xt=Y({name:"LessonContent",description:"Content block within a lesson.",schema:"lssm_learning",map:"lesson_content",fields:{id:b.id({description:"Unique content identifier"}),lessonId:b.foreignKey({description:"Parent lesson"}),contentType:b.enum("ContentType",{description:"Content format"}),content:b.string({isOptional:!0,description:"Text content (markdown, etc.)"}),mediaUrl:b.string({isOptional:!0,description:"Media URL for video/audio"}),embedData:b.json({isOptional:!0,description:"Embed data for external content"}),order:b.int({default:0,description:"Display order"}),duration:b.int({isOptional:!0,description:"Content duration in seconds"}),metadata:b.json({isOptional:!0,description:"Additional metadata"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),lesson:b.belongsTo("Lesson",["lessonId"],["id"],{onDelete:"Cascade"})},indexes:[Z.on(["lessonId","order"])],enums:[Zr]}),$r=[Mt,Wt,mt,Xt],Mr=[xr,Vr,Lr,Zr];import{defineEntity as S,defineEntityEnum as zt,field as a,index as V}from"@contractspec/lib.schema";var Wr=zt({name:"CardRating",values:["AGAIN","HARD","GOOD","EASY"],schema:"lssm_learning",description:"Rating for a flashcard review."}),Ft=S({name:"Deck",description:"A collection of flashcards.",schema:"lssm_learning",map:"deck",fields:{id:a.id({description:"Unique deck identifier"}),ownerId:a.foreignKey({description:"Deck owner (learner)"}),title:a.string({description:"Deck title"}),description:a.string({isOptional:!0,description:"Deck description"}),category:a.string({isOptional:!0,description:"Deck category"}),tags:a.json({isOptional:!0,description:"Tags for discovery"}),isPublic:a.boolean({default:!1,description:"Whether deck is publicly visible"}),cardCount:a.int({default:0,description:"Number of cards"}),coverImageUrl:a.string({isOptional:!0,description:"Cover image URL"}),orgId:a.string({isOptional:!0,description:"Organization scope"}),newCardsPerDay:a.int({default:20,description:"New cards to introduce per day"}),reviewsPerDay:a.int({default:100,description:"Maximum reviews per day"}),metadata:a.json({isOptional:!0,description:"Additional metadata"}),createdAt:a.createdAt(),updatedAt:a.updatedAt(),owner:a.belongsTo("Learner",["ownerId"],["id"],{onDelete:"Cascade"}),cards:a.hasMany("Card")},indexes:[V.on(["ownerId"]),V.on(["isPublic","category"]),V.on(["orgId"])]}),Qt=S({name:"Card",description:"An individual flashcard.",schema:"lssm_learning",map:"card",fields:{id:a.id({description:"Unique card identifier"}),deckId:a.foreignKey({description:"Parent deck"}),front:a.string({description:"Front of card (question)"}),back:a.string({description:"Back of card (answer)"}),hints:a.json({isOptional:!0,description:"Hints for the card"}),explanation:a.string({isOptional:!0,description:"Detailed explanation"}),frontMediaUrl:a.string({isOptional:!0,description:"Media for front"}),backMediaUrl:a.string({isOptional:!0,description:"Media for back"}),audioUrl:a.string({isOptional:!0,description:"Audio pronunciation"}),tags:a.json({isOptional:!0,description:"Card tags"}),difficulty:a.int({default:0,description:"Card difficulty (0-5)"}),order:a.int({default:0,description:"Card order in deck"}),isSuspended:a.boolean({default:!1,description:"Whether card is suspended"}),metadata:a.json({isOptional:!0,description:"Additional metadata"}),createdAt:a.createdAt(),updatedAt:a.updatedAt(),deck:a.belongsTo("Deck",["deckId"],["id"],{onDelete:"Cascade"}),reviews:a.hasMany("CardReview"),schedules:a.hasMany("CardSchedule")},indexes:[V.on(["deckId","order"]),V.on(["isSuspended"])]}),Ut=S({name:"CardReview",description:"A single review of a flashcard.",schema:"lssm_learning",map:"card_review",fields:{id:a.id({description:"Unique review identifier"}),learnerId:a.foreignKey({description:"Reviewer"}),cardId:a.foreignKey({description:"Reviewed card"}),rating:a.enum("CardRating",{description:"Review rating"}),responseTimeMs:a.int({isOptional:!0,description:"Time to respond in ms"}),intervalBefore:a.int({description:"Interval before review (days)"}),easeFactorBefore:a.decimal({description:"Ease factor before review"}),intervalAfter:a.int({description:"Interval after review (days)"}),easeFactorAfter:a.decimal({description:"Ease factor after review"}),reviewType:a.string({default:'"review"',description:"Type: new, learning, review, relearning"}),reviewedAt:a.dateTime({description:"When reviewed"}),createdAt:a.createdAt(),learner:a.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),card:a.belongsTo("Card",["cardId"],["id"],{onDelete:"Cascade"})},indexes:[V.on(["learnerId","reviewedAt"]),V.on(["cardId","reviewedAt"]),V.on(["rating"])],enums:[Wr]}),wt=S({name:"CardSchedule",description:"SRS schedule for a learner/card pair.",schema:"lssm_learning",map:"card_schedule",fields:{id:a.id({description:"Unique schedule identifier"}),learnerId:a.foreignKey({description:"Learner"}),cardId:a.foreignKey({description:"Card"}),interval:a.int({default:0,description:"Current interval in days"}),easeFactor:a.decimal({default:2.5,description:"Ease factor (SM-2)"}),repetitions:a.int({default:0,description:"Number of successful repetitions"}),nextReviewAt:a.dateTime({description:"When next review is due"}),lastReviewAt:a.dateTime({isOptional:!0,description:"When last reviewed"}),learningStep:a.int({default:0,description:"Current learning step"}),isGraduated:a.boolean({default:!1,description:"Whether card has graduated"}),isRelearning:a.boolean({default:!1,description:"Whether card is being relearned"}),lapses:a.int({default:0,description:"Number of times card was forgotten"}),reviewCount:a.int({default:0,description:"Total review count"}),createdAt:a.createdAt(),updatedAt:a.updatedAt(),learner:a.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),card:a.belongsTo("Card",["cardId"],["id"],{onDelete:"Cascade"})},indexes:[V.unique(["learnerId","cardId"],{name:"card_schedule_unique"}),V.on(["learnerId","nextReviewAt"]),V.on(["nextReviewAt"])]}),mr=[Ft,Qt,Ut,wt],Xr=[Wr];import{defineEntity as W,defineEntityEnum as zr,field as n,index as i}from"@contractspec/lib.schema";var Fr=zr({name:"AchievementType",values:["MILESTONE","STREAK","SKILL","SOCIAL","SPECIAL","SEASONAL"],schema:"lssm_learning",description:"Type of achievement."}),Qr=zr({name:"LeaderboardPeriod",values:["DAILY","WEEKLY","MONTHLY","ALL_TIME"],schema:"lssm_learning",description:"Leaderboard time period."}),vt=W({name:"Achievement",description:"An achievement that can be unlocked.",schema:"lssm_learning",map:"achievement",fields:{id:n.id({description:"Unique achievement identifier"}),key:n.string({isUnique:!0,description:"Achievement key"}),name:n.string({description:"Achievement name"}),description:n.string({description:"Achievement description"}),icon:n.string({isOptional:!0,description:"Icon name or URL"}),color:n.string({isOptional:!0,description:"Display color"}),badgeUrl:n.string({isOptional:!0,description:"Badge image URL"}),type:n.enum("AchievementType",{default:"MILESTONE",description:"Achievement type"}),category:n.string({isOptional:!0,description:"Achievement category"}),rarity:n.string({default:'"common"',description:"Rarity: common, rare, epic, legendary"}),xpReward:n.int({default:50,description:"XP awarded"}),condition:n.json({description:"Unlock condition"}),order:n.int({default:0,description:"Display order"}),isHidden:n.boolean({default:!1,description:"Hide until unlocked"}),isActive:n.boolean({default:!0,description:"Whether achievement is active"}),orgId:n.string({isOptional:!0,description:"Organization scope"}),metadata:n.json({isOptional:!0,description:"Additional metadata"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learnerAchievements:n.hasMany("LearnerAchievement")},indexes:[i.on(["type"]),i.on(["category"]),i.on(["isActive"]),i.on(["orgId"])],enums:[Fr]}),Yt=W({name:"LearnerAchievement",description:"An achievement unlocked by a learner.",schema:"lssm_learning",map:"learner_achievement",fields:{id:n.id({description:"Unique record identifier"}),learnerId:n.foreignKey({description:"Learner"}),achievementId:n.foreignKey({description:"Achievement"}),xpEarned:n.int({default:0,description:"XP earned"}),progress:n.int({default:100,description:"Progress percentage"}),currentValue:n.int({isOptional:!0,description:"Current value towards goal"}),targetValue:n.int({isOptional:!0,description:"Target value for completion"}),unlockedAt:n.dateTime({description:"When unlocked"}),createdAt:n.createdAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),achievement:n.belongsTo("Achievement",["achievementId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId","achievementId"],{name:"learner_achievement_unique"}),i.on(["learnerId","unlockedAt"]),i.on(["achievementId"])]}),Gt=W({name:"Streak",description:"Tracks daily learning streaks.",schema:"lssm_learning",map:"streak",fields:{id:n.id({description:"Unique streak identifier"}),learnerId:n.foreignKey({description:"Learner"}),currentStreak:n.int({default:0,description:"Current streak days"}),longestStreak:n.int({default:0,description:"Longest streak ever"}),lastActivityAt:n.dateTime({isOptional:!0,description:"Last learning activity"}),lastActivityDate:n.string({isOptional:!0,description:"Last activity date (YYYY-MM-DD)"}),freezesRemaining:n.int({default:0,description:"Streak freezes available"}),freezeUsedAt:n.dateTime({isOptional:!0,description:"When last freeze was used"}),streakHistory:n.json({isOptional:!0,description:"Historical streak data"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId"],{name:"streak_learner_unique"}),i.on(["currentStreak"]),i.on(["longestStreak"])]}),St=W({name:"DailyGoal",description:"Daily XP goal tracking.",schema:"lssm_learning",map:"daily_goal",fields:{id:n.id({description:"Unique goal identifier"}),learnerId:n.foreignKey({description:"Learner"}),date:n.string({description:"Date (YYYY-MM-DD)"}),targetXp:n.int({description:"Target XP for the day"}),currentXp:n.int({default:0,description:"XP earned today"}),isCompleted:n.boolean({default:!1,description:"Whether goal was met"}),completedAt:n.dateTime({isOptional:!0,description:"When goal was completed"}),xpBreakdown:n.json({isOptional:!0,description:"XP sources breakdown"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId","date"],{name:"daily_goal_unique"}),i.on(["date","isCompleted"])]}),Rt=W({name:"LeaderboardEntry",description:"Leaderboard entry for a learner.",schema:"lssm_learning",map:"leaderboard_entry",fields:{id:n.id({description:"Unique entry identifier"}),learnerId:n.foreignKey({description:"Learner"}),periodType:n.enum("LeaderboardPeriod",{description:"Period type"}),periodKey:n.string({description:"Period key (e.g., 2024-W01)"}),xp:n.int({default:0,description:"XP earned in period"}),rank:n.int({isOptional:!0,description:"Rank in leaderboard"}),lessonsCompleted:n.int({default:0,description:"Lessons completed"}),quizzesPassed:n.int({default:0,description:"Quizzes passed"}),cardsReviewed:n.int({default:0,description:"Cards reviewed"}),streakDays:n.int({default:0,description:"Streak days in period"}),league:n.string({isOptional:!0,description:"League tier"}),orgId:n.string({isOptional:!0,description:"Organization scope"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId","periodType","periodKey"],{name:"leaderboard_entry_unique"}),i.on(["periodType","periodKey","xp"]),i.on(["orgId","periodType","periodKey","xp"])],enums:[Qr]}),Nt=W({name:"Heart",description:"Lives/hearts system for quiz attempts.",schema:"lssm_learning",map:"heart",fields:{id:n.id({description:"Unique heart record identifier"}),learnerId:n.foreignKey({description:"Learner"}),current:n.int({default:5,description:"Current hearts"}),max:n.int({default:5,description:"Maximum hearts"}),lastRefillAt:n.dateTime({isOptional:!0,description:"Last refill time"}),nextRefillAt:n.dateTime({isOptional:!0,description:"Next refill time"}),refillIntervalMinutes:n.int({default:240,description:"Minutes between refills"}),infiniteUntil:n.dateTime({isOptional:!0,description:"Infinite hearts until"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId"],{name:"heart_learner_unique"}),i.on(["nextRefillAt"])]}),Tt=W({name:"XPTransaction",description:"Record of XP earned or spent.",schema:"lssm_learning",map:"xp_transaction",fields:{id:n.id({description:"Unique transaction identifier"}),learnerId:n.foreignKey({description:"Learner"}),amount:n.int({description:"XP amount (positive = earned, negative = spent)"}),type:n.string({description:"Transaction type (lesson, quiz, streak, achievement, etc.)"}),sourceType:n.string({isOptional:!0,description:"Source entity type"}),sourceId:n.string({isOptional:!0,description:"Source entity ID"}),description:n.string({isOptional:!0,description:"Human-readable description"}),balanceAfter:n.int({description:"Total XP after transaction"}),createdAt:n.createdAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.on(["learnerId","createdAt"]),i.on(["type"]),i.on(["sourceType","sourceId"])]}),Ur=[vt,Yt,Gt,St,Rt,Nt,Tt],wr=[Fr,Qr];import{defineEntity as F,defineEntityEnum as vr,field as p,index as _}from"@contractspec/lib.schema";var Yr=vr({name:"EnrollmentStatus",values:["ENROLLED","IN_PROGRESS","COMPLETED","DROPPED","EXPIRED"],schema:"lssm_learning",description:"Status of a course enrollment."}),Gr=vr({name:"ProgressStatus",values:["NOT_STARTED","IN_PROGRESS","COMPLETED","SKIPPED"],schema:"lssm_learning",description:"Status of lesson progress."}),dt=F({name:"Learner",description:"A learner profile.",schema:"lssm_learning",map:"learner",fields:{id:p.id({description:"Unique learner identifier"}),userId:p.string({isUnique:!0,description:"Associated user ID"}),displayName:p.string({isOptional:!0,description:"Display name"}),avatarUrl:p.string({isOptional:!0,description:"Avatar URL"}),bio:p.string({isOptional:!0,description:"Short bio"}),level:p.int({default:1,description:"Current level"}),totalXp:p.int({default:0,description:"Total XP earned"}),currentStreak:p.int({default:0,description:"Current streak days"}),longestStreak:p.int({default:0,description:"Longest streak ever"}),lastActivityAt:p.dateTime({isOptional:!0,description:"Last learning activity"}),locale:p.string({isOptional:!0,description:'Preferred locale for learning content (e.g. "en", "fr", "es")'}),timezone:p.string({default:'"UTC"',description:"Learner timezone"}),dailyGoalXp:p.int({default:50,description:"Daily XP goal"}),reminderEnabled:p.boolean({default:!0,description:"Enable reminders"}),reminderTime:p.string({isOptional:!0,description:"Preferred reminder time"}),orgId:p.string({isOptional:!0,description:"Organization scope"}),metadata:p.json({isOptional:!0,description:"Additional metadata"}),createdAt:p.createdAt(),updatedAt:p.updatedAt(),enrollments:p.hasMany("Enrollment"),lessonProgress:p.hasMany("LessonProgress"),achievements:p.hasMany("LearnerAchievement"),decks:p.hasMany("Deck"),profile:p.hasOne("LearnerProfile")},indexes:[_.on(["orgId"]),_.on(["totalXp"]),_.on(["level"]),_.on(["currentStreak"])]}),Et=F({name:"Enrollment",description:"A learner enrollment in a course.",schema:"lssm_learning",map:"enrollment",fields:{id:p.id({description:"Unique enrollment identifier"}),learnerId:p.foreignKey({description:"Enrolled learner"}),courseId:p.foreignKey({description:"Enrolled course"}),status:p.enum("EnrollmentStatus",{default:"ENROLLED",description:"Enrollment status"}),progress:p.int({default:0,description:"Completion percentage (0-100)"}),completedLessons:p.int({default:0,description:"Number of completed lessons"}),totalLessons:p.int({default:0,description:"Total lessons in course"}),xpEarned:p.int({default:0,description:"XP earned in this course"}),startedAt:p.dateTime({isOptional:!0,description:"When learner started"}),completedAt:p.dateTime({isOptional:!0,description:"When learner completed"}),lastAccessedAt:p.dateTime({isOptional:!0,description:"Last access time"}),certificateId:p.string({isOptional:!0,description:"Issued certificate ID"}),metadata:p.json({isOptional:!0,description:"Additional metadata"}),createdAt:p.createdAt(),updatedAt:p.updatedAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),course:p.belongsTo("Course",["courseId"],["id"],{onDelete:"Cascade"})},indexes:[_.unique(["learnerId","courseId"],{name:"enrollment_unique"}),_.on(["learnerId","status"]),_.on(["courseId","status"])],enums:[Yr]}),ft=F({name:"LessonProgress",description:"Progress tracking for a lesson.",schema:"lssm_learning",map:"lesson_progress",fields:{id:p.id({description:"Unique progress identifier"}),learnerId:p.foreignKey({description:"Learner"}),lessonId:p.foreignKey({description:"Lesson"}),status:p.enum("ProgressStatus",{default:"NOT_STARTED",description:"Progress status"}),progress:p.int({default:0,description:"Completion percentage (0-100)"}),score:p.int({isOptional:!0,description:"Score achieved (for quizzes)"}),attempts:p.int({default:0,description:"Number of attempts"}),bestScore:p.int({isOptional:!0,description:"Best score across attempts"}),timeSpent:p.int({default:0,description:"Time spent in seconds"}),xpEarned:p.int({default:0,description:"XP earned from this lesson"}),startedAt:p.dateTime({isOptional:!0,description:"When started"}),completedAt:p.dateTime({isOptional:!0,description:"When completed"}),lastAccessedAt:p.dateTime({isOptional:!0,description:"Last access time"}),bookmarks:p.json({isOptional:!0,description:"Content bookmarks"}),notes:p.string({isOptional:!0,description:"Learner notes"}),createdAt:p.createdAt(),updatedAt:p.updatedAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),lesson:p.belongsTo("Lesson",["lessonId"],["id"],{onDelete:"Cascade"})},indexes:[_.unique(["learnerId","lessonId"],{name:"lesson_progress_unique"}),_.on(["learnerId","status"]),_.on(["lessonId"])],enums:[Gr]}),lt=F({name:"ModuleCompletion",description:"Module completion record.",schema:"lssm_learning",map:"module_completion",fields:{id:p.id({description:"Unique completion identifier"}),learnerId:p.foreignKey({description:"Learner"}),moduleId:p.foreignKey({description:"Module"}),score:p.int({isOptional:!0,description:"Average score"}),xpEarned:p.int({default:0,description:"XP earned"}),timeSpent:p.int({default:0,description:"Time spent in seconds"}),completedAt:p.dateTime({description:"When completed"}),createdAt:p.createdAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),module:p.belongsTo("CourseModule",["moduleId"],["id"],{onDelete:"Cascade"})},indexes:[_.unique(["learnerId","moduleId"],{name:"module_completion_unique"}),_.on(["learnerId","completedAt"])]}),rs=F({name:"Certificate",description:"Course completion certificate.",schema:"lssm_learning",map:"certificate",fields:{id:p.id({description:"Unique certificate identifier"}),learnerId:p.foreignKey({description:"Certificate holder"}),courseId:p.foreignKey({description:"Completed course"}),enrollmentId:p.foreignKey({description:"Associated enrollment"}),certificateNumber:p.string({isUnique:!0,description:"Unique certificate number"}),title:p.string({description:"Certificate title"}),description:p.string({isOptional:!0,description:"Certificate description"}),score:p.int({isOptional:!0,description:"Final score"}),grade:p.string({isOptional:!0,description:"Grade awarded"}),issuedAt:p.dateTime({description:"When issued"}),validUntil:p.dateTime({isOptional:!0,description:"Expiration date"}),verificationUrl:p.string({isOptional:!0,description:"Verification URL"}),credentialHash:p.string({isOptional:!0,description:"Credential hash for verification"}),isRevoked:p.boolean({default:!1,description:"Whether certificate is revoked"}),revokedAt:p.dateTime({isOptional:!0,description:"When revoked"}),revokedReason:p.string({isOptional:!0,description:"Revocation reason"}),metadata:p.json({isOptional:!0,description:"Additional metadata"}),createdAt:p.createdAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),course:p.belongsTo("Course",["courseId"],["id"],{onDelete:"Cascade"})},indexes:[_.on(["learnerId"]),_.on(["courseId"]),_.on(["issuedAt"])]}),Sr=[dt,Et,ft,lt,rs],Rr=[Yr,Gr];import{defineEntity as R,defineEntityEnum as ts,field as J,index as $}from"@contractspec/lib.schema";var ss=ts({name:"JourneyStepStatus",values:["LOCKED","AVAILABLE","COMPLETED","MISSED","SKIPPED","BLOCKED"],schema:"lssm_learning",description:"Adaptive runtime status of a journey step."}),os=R({name:"JourneyTrack",description:"Adaptive journey track definition.",schema:"lssm_learning",map:"journey_track",fields:{id:J.id({description:"Unique track identifier"}),productId:J.string({isOptional:!0,description:"Product scope"}),name:J.string({description:"Track name"}),description:J.string({isOptional:!0,description:"Track description"}),targetUserSegment:J.string({isOptional:!0,description:"Target learner segment"}),targetRole:J.string({isOptional:!0,description:"Target learner role"}),isActive:J.boolean({default:!0,description:"Whether the track is active"}),isRequired:J.boolean({default:!1,description:"Whether the track must be completed"}),canSkip:J.boolean({default:!1,description:"Whether steps can be skipped"}),totalXp:J.int({default:0,description:"Declared XP ceiling for the track"}),streakRule:J.json({isOptional:!0,description:"Tempo rule used for streak-based rewards"}),completionRewards:J.json({isOptional:!0,description:"Reward applied when the track is completed"}),metadata:J.json({isOptional:!0,description:"Additional track metadata"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),steps:J.hasMany("JourneyStep"),progress:J.hasMany("JourneyProgress")},indexes:[$.on(["productId","isActive"]),$.unique(["productId","targetUserSegment","targetRole"],{name:"journey_track_target"})]}),es=R({name:"JourneyStep",description:"Adaptive journey step definition.",schema:"lssm_learning",map:"journey_step",fields:{id:J.id({description:"Unique step identifier"}),trackId:J.foreignKey({description:"Parent journey track"}),title:J.string({description:"Step title"}),description:J.string({isOptional:!0,description:"Step description"}),instructions:J.string({isOptional:!0,description:"How the learner should complete the step"}),helpUrl:J.string({isOptional:!0,description:"Help or destination URL"}),order:J.int({default:0,description:"Display order"}),completion:J.json({description:"Structured completion condition for this step"}),availability:J.json({isOptional:!0,description:"Unlock and due-window configuration"}),prerequisites:J.json({isOptional:!0,description:"Prerequisites that must be satisfied to unlock the step"}),prerequisiteMode:J.string({isOptional:!0,description:"How to combine prerequisites (all|any)"}),branches:J.json({isOptional:!0,description:"Branch outcomes evaluated when the step completes"}),reward:J.json({isOptional:!0,description:"Reward granted by this step"}),xpReward:J.int({isOptional:!0,description:"Legacy XP reward field"}),isRequired:J.boolean({default:!0,description:"Whether the step is required"}),canSkip:J.boolean({default:!1,description:"Whether the step can be skipped"}),actionUrl:J.string({isOptional:!0,description:"Manual completion action URL"}),actionLabel:J.string({isOptional:!0,description:"Manual action label"}),metadata:J.json({isOptional:!0,description:"Additional step metadata"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),track:J.belongsTo("JourneyTrack",["trackId"],["id"],{onDelete:"Cascade"})},indexes:[$.on(["trackId","order"])]}),ns=R({name:"JourneyProgress",description:"Persisted learner progress through an adaptive journey.",schema:"lssm_learning",map:"journey_progress",fields:{id:J.id({description:"Unique progress identifier"}),learnerId:J.foreignKey({description:"Learner owning this journey state"}),trackId:J.foreignKey({description:"Journey track"}),progressPercent:J.int({default:0,description:"Completion percentage"}),isCompleted:J.boolean({default:!1,description:"Whether the track is completed"}),nextStepId:J.string({isOptional:!0,description:"Next actionable step projected by the engine"}),xpEarned:J.int({default:0,description:"Total XP earned so far"}),badges:J.json({default:"[]",description:"Unlocked badge keys"}),streakState:J.json({default:"{}",description:"Serialized streak engine state"}),eventLog:J.json({default:"[]",description:"Recorded events used to evaluate adaptive progress"}),completionRewardApplied:J.boolean({default:!1,description:"Whether track completion rewards were already applied"}),startedAt:J.dateTime({isOptional:!0,description:"Track start timestamp"}),completedAt:J.dateTime({isOptional:!0,description:"Track completion timestamp"}),lastActivityAt:J.dateTime({isOptional:!0,description:"Latest event or manual advancement timestamp"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),learner:J.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),track:J.belongsTo("JourneyTrack",["trackId"],["id"],{onDelete:"Cascade"}),stepProgress:J.hasMany("JourneyStepProgress")},indexes:[$.unique(["learnerId","trackId"],{name:"journey_progress_unique"}),$.on(["learnerId","isCompleted"]),$.on(["trackId"])]}),ps=R({name:"JourneyStepProgress",description:"Persisted step-level state for an adaptive journey.",schema:"lssm_learning",map:"journey_step_progress",fields:{id:J.id({description:"Unique step-progress identifier"}),progressId:J.foreignKey({description:"Parent journey progress record"}),stepId:J.foreignKey({description:"Referenced journey step"}),status:J.enum("JourneyStepStatus",{description:"Current step status"}),selectedBranchKey:J.string({isOptional:!0,description:"Selected branch outcome for this step"}),xpEarned:J.int({default:0,description:"XP earned from this step"}),occurrences:J.int({default:0,description:"Matching event occurrences"}),masteryCount:J.int({default:0,description:"Accumulated mastery hits"}),availableAt:J.dateTime({isOptional:!0,description:"When the step unlocked"}),dueAt:J.dateTime({isOptional:!0,description:"When the step expires"}),completedAt:J.dateTime({isOptional:!0,description:"When the step completed"}),skippedAt:J.dateTime({isOptional:!0,description:"When the step was skipped"}),blockedAt:J.dateTime({isOptional:!0,description:"When the step was blocked"}),missedAt:J.dateTime({isOptional:!0,description:"When the step was missed"}),triggeringEvent:J.string({isOptional:!0,description:"Event that completed the step"}),eventPayload:J.json({isOptional:!0,description:"Event payload snapshot"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),progress:J.belongsTo("JourneyProgress",["progressId"],["id"],{onDelete:"Cascade"}),step:J.belongsTo("JourneyStep",["stepId"],["id"],{onDelete:"Cascade"})},indexes:[$.unique(["progressId","stepId"],{name:"journey_step_progress_unique"}),$.on(["status"])]}),Nr=[os,es,ns,ps],Tr=[ss];import{defineEntity as Q,defineEntityEnum as sr,field as g,index as x}from"@contractspec/lib.schema";var dr=sr({name:"QuestionType",values:["MULTIPLE_CHOICE","TRUE_FALSE","FILL_BLANK","MATCHING","SHORT_ANSWER","CODE"],schema:"lssm_learning",description:"Type of quiz question."}),Er=sr({name:"QuizStatus",values:["DRAFT","PUBLISHED","ARCHIVED"],schema:"lssm_learning",description:"Publication status of a quiz."}),fr=sr({name:"AttemptStatus",values:["IN_PROGRESS","COMPLETED","TIMED_OUT","ABANDONED"],schema:"lssm_learning",description:"Status of a quiz attempt."}),us=Q({name:"Quiz",description:"A quiz assessment.",schema:"lssm_learning",map:"quiz",fields:{id:g.id({description:"Unique quiz identifier"}),lessonId:g.foreignKey({isOptional:!0,description:"Associated lesson"}),title:g.string({description:"Quiz title"}),description:g.string({isOptional:!0,description:"Quiz description"}),instructions:g.string({isOptional:!0,description:"Quiz instructions"}),passingScore:g.int({default:70,description:"Passing score percentage"}),timeLimit:g.int({isOptional:!0,description:"Time limit in seconds"}),maxAttempts:g.int({isOptional:!0,description:"Maximum attempts allowed"}),shuffleQuestions:g.boolean({default:!1,description:"Shuffle question order"}),shuffleOptions:g.boolean({default:!1,description:"Shuffle answer options"}),showCorrectAnswers:g.boolean({default:!0,description:"Show correct answers after"}),showExplanations:g.boolean({default:!0,description:"Show explanations after"}),status:g.enum("QuizStatus",{default:"DRAFT",description:"Publication status"}),totalPoints:g.int({default:0,description:"Total points available"}),xpReward:g.int({default:20,description:"XP for passing"}),orgId:g.string({isOptional:!0,description:"Organization scope"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),lesson:g.belongsTo("Lesson",["lessonId"],["id"],{onDelete:"Cascade"}),questions:g.hasMany("Question"),attempts:g.hasMany("QuizAttempt")},indexes:[x.on(["lessonId"]),x.on(["status"]),x.on(["orgId"])],enums:[Er]}),cs=Q({name:"Question",description:"A quiz question.",schema:"lssm_learning",map:"question",fields:{id:g.id({description:"Unique question identifier"}),quizId:g.foreignKey({description:"Parent quiz"}),type:g.enum("QuestionType",{description:"Question type"}),content:g.string({description:"Question text"}),mediaUrl:g.string({isOptional:!0,description:"Question media"}),points:g.int({default:1,description:"Points for correct answer"}),codeLanguage:g.string({isOptional:!0,description:"Programming language"}),codeTemplate:g.string({isOptional:!0,description:"Starter code template"}),testCases:g.json({isOptional:!0,description:"Test cases for code validation"}),explanation:g.string({isOptional:!0,description:"Explanation of correct answer"}),hint:g.string({isOptional:!0,description:"Hint for the question"}),order:g.int({default:0,description:"Display order"}),skillId:g.string({isOptional:!0,description:"Associated skill"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),quiz:g.belongsTo("Quiz",["quizId"],["id"],{onDelete:"Cascade"}),options:g.hasMany("QuestionOption")},indexes:[x.on(["quizId","order"]),x.on(["type"]),x.on(["skillId"])],enums:[dr]}),gs=Q({name:"QuestionOption",description:"An answer option for a question.",schema:"lssm_learning",map:"question_option",fields:{id:g.id({description:"Unique option identifier"}),questionId:g.foreignKey({description:"Parent question"}),content:g.string({description:"Option text"}),matchContent:g.string({isOptional:!0,description:"Match pair content"}),isCorrect:g.boolean({default:!1,description:"Whether option is correct"}),feedback:g.string({isOptional:!0,description:"Feedback when selected"}),order:g.int({default:0,description:"Display order"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),question:g.belongsTo("Question",["questionId"],["id"],{onDelete:"Cascade"})},indexes:[x.on(["questionId","order"])]}),ks=Q({name:"QuizAttempt",description:"A learner quiz attempt.",schema:"lssm_learning",map:"quiz_attempt",fields:{id:g.id({description:"Unique attempt identifier"}),learnerId:g.foreignKey({description:"Learner"}),quizId:g.foreignKey({description:"Quiz"}),status:g.enum("AttemptStatus",{default:"IN_PROGRESS",description:"Attempt status"}),score:g.int({isOptional:!0,description:"Score achieved"}),percentageScore:g.int({isOptional:!0,description:"Percentage score"}),passed:g.boolean({isOptional:!0,description:"Whether passed"}),totalQuestions:g.int({default:0,description:"Total questions"}),answeredQuestions:g.int({default:0,description:"Questions answered"}),correctAnswers:g.int({default:0,description:"Correct answers"}),answers:g.json({isOptional:!0,description:"Submitted answers"}),xpEarned:g.int({default:0,description:"XP earned"}),timeSpent:g.int({default:0,description:"Time spent in seconds"}),startedAt:g.dateTime({description:"When started"}),completedAt:g.dateTime({isOptional:!0,description:"When completed"}),attemptNumber:g.int({default:1,description:"Which attempt this is"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),learner:g.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),quiz:g.belongsTo("Quiz",["quizId"],["id"],{onDelete:"Cascade"})},indexes:[x.on(["learnerId","quizId"]),x.on(["learnerId","status"]),x.on(["quizId","status"])],enums:[fr]}),Js=Q({name:"SkillAssessment",description:"Assessment of a skill based on quiz performance.",schema:"lssm_learning",map:"skill_assessment",fields:{id:g.id({description:"Unique assessment identifier"}),learnerId:g.foreignKey({description:"Learner"}),skillId:g.string({description:"Skill identifier"}),skillName:g.string({description:"Skill name"}),level:g.int({default:1,description:"Proficiency level (1-5)"}),score:g.int({default:0,description:"Assessment score (0-100)"}),confidence:g.decimal({default:0.5,description:"Confidence in assessment"}),questionsAnswered:g.int({default:0,description:"Total questions answered"}),questionsCorrect:g.int({default:0,description:"Total correct"}),assessedAt:g.dateTime({description:"Last assessment time"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),learner:g.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[x.unique(["learnerId","skillId"],{name:"skill_assessment_unique"}),x.on(["learnerId","level"]),x.on(["skillId"])]}),lr=[us,cs,gs,ks,Js],rt=[dr,Er,fr];var hs=[...$r,...Sr,...Nr,...mr,...lr,...Ur,...qr],bs=[...Mr,...Rr,...Tr,...Xr,...rt,...wr,...Hr],Ke={moduleId:"@contractspec/module.learning-journey",entities:hs,enums:bs};import{defineEvent as K}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as C,ScalarTypeEnum as u}from"@contractspec/lib.schema";var as=C({name:"CoursePublishedEventPayload",description:"Payload when a course is published",fields:{courseId:{type:u.String_unsecure(),isOptional:!1},title:{type:u.String_unsecure(),isOptional:!1},authorId:{type:u.String_unsecure(),isOptional:!1},publishedAt:{type:u.DateTime(),isOptional:!1}}}),Is=K({meta:{key:"course.published",version:"1.0.0",description:"A course has been published.",stability:"stable",owners:[...P],tags:["learning","course"]},payload:as}),As=C({name:"EnrollmentCreatedEventPayload",description:"Payload when a learner enrolls in a course",fields:{enrollmentId:{type:u.String_unsecure(),isOptional:!1},learnerId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},enrolledAt:{type:u.DateTime(),isOptional:!1}}}),Os=K({meta:{key:"enrollment.created",version:"1.0.0",description:"A learner has enrolled in a course.",stability:"stable",owners:[...P],tags:["learning","enrollment"]},payload:As}),Ps=C({name:"LessonCompletedEventPayload",description:"Payload when a lesson is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},lessonId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},score:{type:u.Int_unsecure(),isOptional:!0},xpEarned:{type:u.Int_unsecure(),isOptional:!1},timeSpent:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),Ds=K({meta:{key:"lesson.completed",version:"1.0.0",description:"A learner has completed a lesson.",stability:"stable",owners:[...P],tags:["learning","progress","lesson"]},payload:Ps}),Bs=C({name:"CourseCompletedEventPayload",description:"Payload when a course is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},enrollmentId:{type:u.String_unsecure(),isOptional:!1},score:{type:u.Int_unsecure(),isOptional:!0},xpEarned:{type:u.Int_unsecure(),isOptional:!1},certificateId:{type:u.String_unsecure(),isOptional:!0},completedAt:{type:u.DateTime(),isOptional:!1}}}),js=K({meta:{key:"course.completed",version:"1.0.0",description:"A learner has completed a course.",stability:"stable",owners:[...P],tags:["learning","progress","course"]},payload:Bs}),is=C({name:"OnboardingStartedEventPayload",description:"Payload when onboarding starts",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},trackId:{type:u.String_unsecure(),isOptional:!1},productId:{type:u.String_unsecure(),isOptional:!1},startedAt:{type:u.DateTime(),isOptional:!1}}}),Ks=K({meta:{key:"onboarding.started",version:"1.0.0",description:"A learner has started onboarding.",stability:"stable",owners:[...P],tags:["learning","onboarding"]},payload:is}),Cs=C({name:"OnboardingStepCompletedEventPayload",description:"Payload when an onboarding step is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},trackId:{type:u.String_unsecure(),isOptional:!1},stepId:{type:u.String_unsecure(),isOptional:!1},triggeringEvent:{type:u.String_unsecure(),isOptional:!0},xpEarned:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),ys=K({meta:{key:"onboarding.step_completed",version:"1.0.0",description:"An onboarding step has been completed.",stability:"stable",owners:[...P],tags:["learning","onboarding"]},payload:Cs}),_s=C({name:"OnboardingCompletedEventPayload",description:"Payload when onboarding is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},trackId:{type:u.String_unsecure(),isOptional:!1},productId:{type:u.String_unsecure(),isOptional:!1},xpEarned:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),qs=K({meta:{key:"onboarding.completed",version:"1.0.0",description:"A learner has completed onboarding.",stability:"stable",owners:[...P],tags:["learning","onboarding"]},payload:_s}),Hs=C({name:"CardReviewedEventPayload",description:"Payload when a flashcard is reviewed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},cardId:{type:u.String_unsecure(),isOptional:!1},deckId:{type:u.String_unsecure(),isOptional:!1},rating:{type:u.String_unsecure(),isOptional:!1},responseTimeMs:{type:u.Int_unsecure(),isOptional:!0},reviewedAt:{type:u.DateTime(),isOptional:!1}}}),xs=K({meta:{key:"flashcard.reviewed",version:"1.0.0",description:"A flashcard has been reviewed.",stability:"stable",owners:[...P],tags:["learning","flashcards"]},payload:Hs}),Vs=C({name:"QuizStartedEventPayload",description:"Payload when a quiz is started",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},quizId:{type:u.String_unsecure(),isOptional:!1},attemptId:{type:u.String_unsecure(),isOptional:!1},attemptNumber:{type:u.Int_unsecure(),isOptional:!1},startedAt:{type:u.DateTime(),isOptional:!1}}}),Ls=K({meta:{key:"quiz.started",version:"1.0.0",description:"A quiz attempt has started.",stability:"stable",owners:[...P],tags:["learning","quiz"]},payload:Vs}),Zs=C({name:"QuizCompletedEventPayload",description:"Payload when a quiz is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},quizId:{type:u.String_unsecure(),isOptional:!1},attemptId:{type:u.String_unsecure(),isOptional:!1},score:{type:u.Int_unsecure(),isOptional:!1},percentageScore:{type:u.Int_unsecure(),isOptional:!1},passed:{type:u.Boolean(),isOptional:!1},xpEarned:{type:u.Int_unsecure(),isOptional:!1},timeSpent:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),$s=K({meta:{key:"quiz.completed",version:"1.0.0",description:"A quiz attempt has been completed.",stability:"stable",owners:[...P],tags:["learning","quiz"]},payload:Zs}),Ms=C({name:"XpEarnedEventPayload",description:"Payload when XP is earned",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},amount:{type:u.Int_unsecure(),isOptional:!1},source:{type:u.String_unsecure(),isOptional:!1},sourceId:{type:u.String_unsecure(),isOptional:!0},totalXp:{type:u.Int_unsecure(),isOptional:!1},earnedAt:{type:u.DateTime(),isOptional:!1}}}),Ws=K({meta:{key:"xp.earned",version:"1.0.0",description:"XP has been earned.",stability:"stable",owners:[...P],tags:["learning","gamification","xp"]},payload:Ms}),ms=C({name:"LevelUpEventPayload",description:"Payload when a learner levels up",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},previousLevel:{type:u.Int_unsecure(),isOptional:!1},newLevel:{type:u.Int_unsecure(),isOptional:!1},totalXp:{type:u.Int_unsecure(),isOptional:!1},leveledUpAt:{type:u.DateTime(),isOptional:!1}}}),Xs=K({meta:{key:"level.up",version:"1.0.0",description:"A learner has leveled up.",stability:"stable",owners:[...P],tags:["learning","gamification","level"]},payload:ms}),zs=C({name:"StreakUpdatedEventPayload",description:"Payload when a streak is updated",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},previousStreak:{type:u.Int_unsecure(),isOptional:!1},currentStreak:{type:u.Int_unsecure(),isOptional:!1},longestStreak:{type:u.Int_unsecure(),isOptional:!1},freezeUsed:{type:u.Boolean(),isOptional:!1},updatedAt:{type:u.DateTime(),isOptional:!1}}}),Fs=K({meta:{key:"streak.updated",version:"1.0.0",description:"A streak has been updated.",stability:"stable",owners:[...P],tags:["learning","gamification","streak"]},payload:zs}),Qs=C({name:"AchievementUnlockedEventPayload",description:"Payload when an achievement is unlocked",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},achievementId:{type:u.String_unsecure(),isOptional:!1},achievementKey:{type:u.String_unsecure(),isOptional:!1},achievementName:{type:u.String_unsecure(),isOptional:!1},xpEarned:{type:u.Int_unsecure(),isOptional:!1},unlockedAt:{type:u.DateTime(),isOptional:!1}}}),Us=K({meta:{key:"achievement.unlocked",version:"1.0.0",description:"An achievement has been unlocked.",stability:"stable",owners:[...P],tags:["learning","gamification","achievement"]},payload:Qs}),ws=C({name:"DailyGoalCompletedEventPayload",description:"Payload when a daily goal is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},date:{type:u.String_unsecure(),isOptional:!1},targetXp:{type:u.Int_unsecure(),isOptional:!1},actualXp:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),vs=K({meta:{key:"daily_goal.completed",version:"1.0.0",description:"A daily goal has been completed.",stability:"stable",owners:[...P],tags:["learning","gamification","goal"]},payload:ws}),Ys=C({name:"CertificateIssuedEventPayload",description:"Payload when a certificate is issued",fields:{certificateId:{type:u.String_unsecure(),isOptional:!1},learnerId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},certificateNumber:{type:u.String_unsecure(),isOptional:!1},issuedAt:{type:u.DateTime(),isOptional:!1}}}),Gs=K({meta:{key:"certificate.issued",version:"1.0.0",description:"A certificate has been issued.",stability:"stable",owners:[...P],tags:["learning","certificate"]},payload:Ys}),We={CoursePublishedEvent:Is,EnrollmentCreatedEvent:Os,LessonCompletedEvent:Ds,CourseCompletedEvent:js,OnboardingStartedEvent:Ks,OnboardingStepCompletedEvent:ys,OnboardingCompletedEvent:qs,CardReviewedEvent:xs,QuizStartedEvent:Ls,QuizCompletedEvent:$s,XpEarnedEvent:Ws,LevelUpEvent:Xs,StreakUpdatedEvent:Fs,AchievementUnlockedEvent:Us,DailyGoalCompletedEvent:vs,CertificateIssuedEvent:Gs};import{defineFeature as Ss}from"@contractspec/lib.contracts-spec";var ze=Ss({meta:{key:"learning-journey",version:"1.0.0",title:"Learning Journey",description:"Learning platform with courses, onboarding, flashcards, and gamification",domain:"learning",owners:["@platform.learning-journey"],tags:["learning","onboarding","courses","flashcards","gamification"],stability:"stable"},operations:[{key:"learning.journey.recordEvent",version:"1.0.0"},{key:"learning.journey.listTracks",version:"1.0.0"},{key:"learning.journey.getProgress",version:"1.0.0"},{key:"learning.enroll",version:"1.0.0"},{key:"learning.completeLesson",version:"1.0.0"},{key:"learning.submitCardReview",version:"1.0.0"},{key:"learning.getDueCards",version:"1.0.0"},{key:"learning.getDashboard",version:"1.0.0"}],events:[{key:"course.published",version:"1.0.0"},{key:"course.completed",version:"1.0.0"},{key:"enrollment.created",version:"1.0.0"},{key:"lesson.completed",version:"1.0.0"},{key:"onboarding.started",version:"1.0.0"},{key:"onboarding.step_completed",version:"1.0.0"},{key:"onboarding.completed",version:"1.0.0"},{key:"flashcard.reviewed",version:"1.0.0"},{key:"quiz.started",version:"1.0.0"},{key:"quiz.completed",version:"1.0.0"},{key:"xp.earned",version:"1.0.0"},{key:"level.up",version:"1.0.0"},{key:"streak.updated",version:"1.0.0"},{key:"achievement.unlocked",version:"1.0.0"},{key:"daily_goal.completed",version:"1.0.0"},{key:"certificate.issued",version:"1.0.0"}],presentations:[],opToPresentation:[],presentationsTargets:[],capabilities:{provides:[{key:"learning-journey",version:"1.0.0"},{key:"onboarding",version:"1.0.0"},{key:"gamification",version:"1.0.0"}],requires:[{key:"identity",version:"1.0.0"}]}});var or=(r,t)=>{if(!r)return!0;if(!t)return!1;return Object.entries(r).every(([o,s])=>t[o]===s)},N=(r,t)=>{if(r.eventName!==t.name)return!1;if(r.eventVersion!==void 0&&t.version!==void 0&&r.eventVersion!==t.version)return!1;if(r.sourceModule&&t.sourceModule&&r.sourceModule!==t.sourceModule)return!1;return or(r.payloadFilter,t.payload)},T=(r,t,o,s)=>{if(r.kind==="count"){if(!N(r,t))return{matched:!1};let c=o.occurrences+1;return{matched:(r.withinHours===void 0||s!==void 0&&t.occurredAt!==void 0&&(t.occurredAt.getTime()-s.getTime())/3600000<=r.withinHours)&&c>=r.atLeast,occurrences:c}}if(r.kind==="time_window"){if(!N(r,t))return{matched:!1};if(r.availableAfterHours!==void 0&&s!==void 0&&t.occurredAt!==void 0&&(t.occurredAt.getTime()-s.getTime())/3600000<r.availableAfterHours)return{matched:!1};if(r.withinHoursOfStart!==void 0&&s!==void 0&&t.occurredAt!==void 0&&(t.occurredAt.getTime()-s.getTime())/3600000>r.withinHoursOfStart)return{matched:!1};return{matched:!0}}if(r.kind==="mastery"){if(t.name!==r.eventName)return{matched:!1};if(!or(r.payloadFilter,t.payload))return{matched:!1};let c=r.masteryField??"mastery",A=t.payload?.[c];if(typeof A!=="number")return{matched:!1};if(A<r.minimumMastery)return{matched:!1};let I=o.masteryCount+1;return{masteryCount:I,matched:I>=(r.requiredCount??1)}}return{matched:N(r,t)}},er=(r,t)=>{if(!r||!t)return{};let o=t.getTime(),s=o;if(r.unlockOnDay!==void 0)s=o+(r.unlockOnDay-1)*24*60*60*1000;if(r.unlockAfterHours!==void 0)s=o+r.unlockAfterHours*60*60*1000;let c=new Date(s),A=r.dueWithinHours!==void 0?new Date(c.getTime()+r.dueWithinHours*60*60*1000):void 0;return{availableAt:c,dueAt:A}},nr=(r,t)=>{let o=t.steps.find((s)=>s.stepId===r.stepId);if(!o)return!1;if(r.kind==="step_completed")return o.status==="COMPLETED"||o.status==="SKIPPED";return o.selectedBranchKey===r.branchKey},pr=(r,t,o,s)=>{if(!r.branches?.length)return;if(!t)return r.branches.find((c)=>c.when===void 0);for(let c of r.branches){if(!c.when)continue;if(T(c.when,t,o,s).matched)return c}return r.branches.find((c)=>c.when===void 0)};var Rs=(r)=>r.map((t)=>({...t})),Ns=(r,t)=>{let o=new Map;for(let s of r.steps){let c=t.steps.find((I)=>I.stepId===s.id),A=s.branches?.find((I)=>I.key===c?.selectedBranchKey);if(!A?.blockStepIds?.length)continue;for(let I of A.blockStepIds)o.set(I,{blockedByBranchKey:A.key,blockedByStepId:s.id})}return o},M=(r,t,o={})=>{let s=o.now??new Date,c={...t,badges:[...t.badges],eventLog:[...t.eventLog],steps:Rs(t.steps),streak:{...t.streak}},A=Ns(r,c);for(let I of r.steps){let O=c.steps.find((X)=>X.stepId===I.id);if(!O)continue;let{availableAt:j,dueAt:D}=er(I.availability,c.startedAt);if(O.availableAt=j,O.dueAt=D,O.status==="COMPLETED"||O.status==="SKIPPED")continue;let q=A.get(I.id);if(q){O.blockedAt??=s,O.blockedByBranchKey=q.blockedByBranchKey,O.blockedByStepId=q.blockedByStepId,O.status="BLOCKED";continue}let m=I.prerequisites??[],B=I.prerequisiteMode==="any"?m.some((X)=>nr(X,c)):m.every((X)=>nr(X,c)),ut=(m.length===0?!0:B)&&(!j||s.getTime()>=j.getTime());if(D&&s.getTime()>D.getTime()){O.missedAt??=s,O.status="MISSED";continue}O.status=ut?"AVAILABLE":"LOCKED"}return c},d=(r)=>{let t=r.steps.filter((o)=>o.status!=="BLOCKED");return t.length>0&&t.every((o)=>o.status==="COMPLETED"||o.status==="SKIPPED")},ur=(r,t,o={})=>{let s=M(r,t,o),c=s.steps.filter((B)=>B.status!=="BLOCKED"),A=s.steps.filter((B)=>B.status==="COMPLETED"),I=s.steps.filter((B)=>B.status==="AVAILABLE").map((B)=>B.stepId),O=s.steps.filter((B)=>B.status==="BLOCKED").map((B)=>B.stepId),j=s.steps.filter((B)=>B.status==="MISSED").map((B)=>B.stepId),D=c.filter((B)=>B.status==="COMPLETED"||B.status==="SKIPPED").length,q=c.length>0?Math.round(D/c.length*100):0,m=I[0]??null;return{activeStepCount:c.length,availableStepIds:I,badges:[...s.badges],blockedStepIds:O,completedAt:s.completedAt,completedStepCount:D,completedStepIds:A.map((B)=>B.stepId),currentStepId:m,isCompleted:d(s),lastActivityAt:s.lastActivityAt,learnerId:s.learnerId,missedStepIds:j,nextStepId:m,progressPercent:q,startedAt:s.startedAt,steps:s.steps,streakDays:s.streak.currentStreak,totalSteps:r.steps.length,trackId:s.trackId,xpEarned:s.xpEarned}};var cr=new U,tt=new v,st=(r)=>({...r,badges:[...r.badges],eventLog:[...r.eventLog],steps:r.steps.map((t)=>({...t})),streak:{...r.streak}}),Ts=(r)=>r.reward?.xp??r.xpReward??0,ot=(r,t)=>r.steps.find((o)=>o.stepId===t),et=(r,t)=>{if(t&&!r.badges.includes(t))r.badges.push(t)},nt=(r,t,o,s)=>{if(!d(t)||t.completionRewardApplied)return t;let c=r.completionRewards?.xp??0;if(c>0)t.xpEarned+=s.calculate({activity:"journey_complete",baseXp:c,currentStreak:t.streak.currentStreak}).totalXp;return et(t,r.completionRewards?.badgeKey),t.completedAt=o,t.completionRewardApplied=!0,t},pt=(r,t,o,s,c,A,I,O)=>{if(s.status==="COMPLETED")return;let j=Ts(o);if(j>0)t.xpEarned+=O.calculate({activity:"journey_step",baseXp:j,currentStreak:t.streak.currentStreak}).totalXp;s.completedAt=c,s.eventPayload=A?.payload,s.manual=I,s.status="COMPLETED",s.triggeringEvent=A?.name??"journey.step.manual",s.xpEarned=j;let D=pr(o,A,s,t.startedAt);if(!D)return;if(s.selectedBranchKey=D.key,D.reward?.xp)t.xpEarned+=D.reward.xp;et(t,D.reward?.badgeKey)},ds=(r,t={})=>M(r,{badges:[],completionRewardApplied:!1,eventLog:[],learnerId:t.learnerId,startedAt:t.now,steps:r.steps.map((o)=>({masteryCount:0,occurrences:0,status:"LOCKED",stepId:o.id,xpEarned:0})),streak:cr.getInitialState(),trackId:r.id,xpEarned:0},{now:t.now}),Es=(r,t,o,s={})=>{let c=s.streakEngine??cr,A=s.xpEngine??tt,I=o.occurredAt??new Date,O=M(r,st(t),{now:I});O.eventLog.push({...o,occurredAt:I,trackId:r.id}),O.lastActivityAt=I,O.startedAt??=I,O.streak=c.update(O.streak,I).state;for(let j of r.steps){let D=ot(O,j.id);if(!D||D.status!=="AVAILABLE")continue;let q=T(j.completion,o,D,O.startedAt);if(q.occurrences!==void 0)D.occurrences=q.occurrences;if(q.masteryCount!==void 0)D.masteryCount=q.masteryCount;if(!q.matched)continue;pt(r,O,j,D,I,o,!1,A)}return nt(r,M(r,O,{now:I}),I,A)},fs=(r,t,o,s={})=>{let c=s.streakEngine??cr,A=s.xpEngine??tt,I=s.now??new Date,O=M(r,st(t),{now:I}),j=r.steps.find((q)=>q.id===o),D=ot(O,o);if(!j||!D||D.status!=="AVAILABLE")return O;return O.eventLog.push({name:"journey.step.manual",occurredAt:I,trackId:r.id}),O.lastActivityAt=I,O.startedAt??=I,O.streak=c.update(O.streak,I).state,pt(r,O,j,D,I,{name:"journey.step.manual",occurredAt:I},!0,A),nt(r,M(r,O,{now:I}),I,A)},ls=(r,t,o={})=>ur(r,t,o);export{To as xpEngine,M as synchronizeJourneyProgressState,Zo as streakEngine,Vo as srsEngine,pr as resolveJourneyBranch,er as resolveJourneyAvailability,Es as recordJourneyEvent,rt as quizEnums,lr as quizEntities,ls as projectJourneyProgress,or as matchesPayloadFilter,T as matchesJourneyCondition,N as matchesBaseJourneyEvent,Ke as learningJourneySchemaContribution,bs as learningJourneyEnums,hs as learningJourneyEntities,Rr as learnerEnums,Sr as learnerEntities,Tr as journeyEnums,Nr as journeyEntities,d as isJourneyComplete,No as getXpSourceLabel,wr as gamificationEnums,Ur as gamificationEntities,Xr as flashcardEnums,mr as flashcardEntities,ds as createJourneyProgressState,Mr as courseEnums,$r as courseEntities,fs as completeJourneyStep,ur as buildJourneyProgressSnapshot,Hr as aiEnums,qr as aiEntities,Ws as XpEarnedEvent,Tt as XPTransactionEntity,v as XPEngine,l as SuccessOutput,br as SubmitCardReviewInput,Do as SubmitCardReviewContract,Fs as StreakUpdatedEvent,Gt as StreakEntity,U as StreakEngine,Vt as SkillMapEntity,Js as SkillAssessmentEntity,Dr as SRSEngine,uo as RecordJourneyEventContract,_r as RecommendationTypeEnum,Zt as RecommendationEntity,Er as QuizStatusEnum,Ls as QuizStartedEvent,us as QuizEntity,$s as QuizCompletedEvent,ks as QuizAttemptEntity,dr as QuestionTypeEnum,gs as QuestionOptionEntity,cs as QuestionEntity,Gr as ProgressStatusEnum,Jo as ProgressModel,ys as OnboardingStepCompletedEvent,Ks as OnboardingStartedEvent,qs as OnboardingCompletedEvent,lt as ModuleCompletionEntity,no as ListJourneyTracksContract,Xs as LevelUpEvent,Lr as LessonTypeEnum,ft as LessonProgressEntity,mt as LessonEntity,Xt as LessonContentEntity,Ds as LessonCompletedEvent,yr as LearningStyleEnum,Lt as LearningPathEntity,ze as LearningJourneyFeature,We as LearningJourneyEvents,$t as LearningGapEntity,xt as LearnerProfileEntity,Ot as LearnerModel,dt as LearnerEntity,Or as LearnerDashboardModel,Yt as LearnerAchievementEntity,Qr as LeaderboardPeriodEnum,Rt as LeaderboardEntryEntity,P as LEARNING_JOURNEY_OWNERS,Jt as JourneyTrackModel,os as JourneyTrackEntity,ss as JourneyStepStatusEnum,ps as JourneyStepProgressEntity,es as JourneyStepEntity,E as JourneyProgressModel,ns as JourneyProgressEntity,Nt as HeartEntity,Ar as GetLearnerDashboardInput,jo as GetLearnerDashboardContract,po as GetJourneyProgressContract,Ir as GetDueCardsOutput,ar as GetDueCardsInput,Bo as GetDueCardsContract,Yr as EnrollmentStatusEnum,f as EnrollmentModel,Et as EnrollmentEntity,Os as EnrollmentCreatedEvent,Jr as EnrollInCourseInput,Oo as EnrollInCourseContract,ho as DeckModel,Ft as DeckEntity,St as DailyGoalEntity,vs as DailyGoalCompletedEvent,w as DEFAULT_XP_CONFIG,Kt as DEFAULT_STREAK_CONFIG,it as DEFAULT_SRS_CONFIG,Vr as CourseStatusEnum,Is as CoursePublishedEvent,Wt as CourseModuleEntity,ko as CourseModel,Mt as CourseEntity,xr as CourseDifficultyEnum,js as CourseCompletedEvent,Zr as ContentTypeEnum,hr as CompleteLessonInput,Po as CompleteLessonContract,Gs as CertificateIssuedEvent,rs as CertificateEntity,wt as CardScheduleEntity,xs as CardReviewedEvent,Ut as CardReviewEntity,Wr as CardRatingEnum,Pt as CardModel,Qt as CardEntity,fr as AttemptStatusEnum,Us as AchievementUnlockedEvent,Fr as AchievementTypeEnum,Dt as AchievementModel,vt as AchievementEntity};
81
+ - Use Feature Flags to trial new tracks or reward rules; default safe/off.`}];Bt(jt);var it={learningSteps:[1,10],graduatingInterval:1,easyInterval:4,relearningSteps:[10],minEaseFactor:1.3,maxInterval:365,intervalModifier:1,newIntervalModifier:0.5,hardIntervalModifier:1.2,easyBonus:1.3};class Dr{config;constructor(r={}){this.config={...it,...r}}calculateNextReview(r,t,o=new Date){if(!r.isGraduated&&!r.isRelearning)return this.handleLearningCard(r,t,o);if(r.isRelearning)return this.handleRelearningCard(r,t,o);return this.handleReviewCard(r,t,o)}getInitialState(){return{interval:0,easeFactor:2.5,repetitions:0,learningStep:0,isGraduated:!1,isRelearning:!1,lapses:0}}isDue(r,t=new Date){return r<=t}getOverdueDays(r,t=new Date){let o=t.getTime()-r.getTime();return Math.floor(o/86400000)}handleLearningCard(r,t,o){let s=this.config.learningSteps,c=r.learningStep,A=!1,I=0,O;switch(t){case"AGAIN":c=0,I=s[0]??1,O=this.addMinutes(o,I);break;case"HARD":I=s[c]??s[0]??1,O=this.addMinutes(o,I);break;case"GOOD":if(c++,c>=s.length)A=!0,I=this.config.graduatingInterval,O=this.addDays(o,I);else I=s[c]??10,O=this.addMinutes(o,I);break;case"EASY":A=!0,I=this.config.easyInterval,O=this.addDays(o,I);break}return{interval:A?I:0,easeFactor:r.easeFactor,repetitions:A?1:0,nextReviewAt:O,learningStep:c,isGraduated:A,isRelearning:!1,lapses:r.lapses}}handleRelearningCard(r,t,o){let s=this.config.relearningSteps,c=r.learningStep,A=!0,I=0,O;switch(t){case"AGAIN":c=0,I=s[0]??10,O=this.addMinutes(o,I);break;case"HARD":I=s[c]??s[0]??10,O=this.addMinutes(o,I);break;case"GOOD":if(c++,c>=s.length)A=!1,I=Math.max(1,Math.floor(r.interval*this.config.newIntervalModifier)),O=this.addDays(o,I);else I=s[c]??10,O=this.addMinutes(o,I);break;case"EASY":A=!1,I=Math.max(1,Math.floor(r.interval*this.config.newIntervalModifier*1.5)),O=this.addDays(o,I);break}return{interval:A?r.interval:I,easeFactor:r.easeFactor,repetitions:A?r.repetitions:r.repetitions+1,nextReviewAt:O,learningStep:c,isGraduated:!0,isRelearning:A,lapses:r.lapses}}handleReviewCard(r,t,o){let s,c=r.easeFactor,A=r.repetitions,I=!1,O=0,j=r.lapses;switch(t){case"AGAIN":return j++,I=!0,O=0,c=Math.max(this.config.minEaseFactor,c-0.2),s=r.interval,{interval:s,easeFactor:c,repetitions:A,nextReviewAt:this.addMinutes(o,this.config.relearningSteps[0]??10),learningStep:O,isGraduated:!0,isRelearning:!0,lapses:j};case"HARD":c=Math.max(this.config.minEaseFactor,c-0.15),s=Math.max(r.interval+1,r.interval*this.config.hardIntervalModifier);break;case"GOOD":s=r.interval*c*this.config.intervalModifier,A++;break;case"EASY":c=c+0.15,s=r.interval*c*this.config.easyBonus*this.config.intervalModifier,A++;break}return s=Math.min(Math.round(s),this.config.maxInterval),s=Math.max(1,s),{interval:s,easeFactor:c,repetitions:A,nextReviewAt:this.addDays(o,s),learningStep:O,isGraduated:!0,isRelearning:I,lapses:j}}addMinutes(r,t){return new Date(r.getTime()+t*60*1000)}addDays(r,t){return new Date(r.getTime()+t*24*60*60*1000)}}var Vo=new Dr;var Kt={timezone:"UTC",freezesPerMonth:2,maxFreezes:5,gracePeriodHours:4};class U{config;constructor(r={}){this.config={...Kt,...r}}update(r,t=new Date){let o=this.getDateString(t),s={state:{...r},streakMaintained:!1,streakLost:!1,freezeUsed:!1,newStreak:!1,daysMissed:0};if(!r.lastActivityDate)return s.state.currentStreak=1,s.state.longestStreak=Math.max(1,r.longestStreak),s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.newStreak=!0,s.streakMaintained=!0,s;if(r.lastActivityDate===o)return s.state.lastActivityAt=t,s.streakMaintained=!0,s;let c=this.getDaysBetween(r.lastActivityDate,o);if(c===1)return s.state.currentStreak=r.currentStreak+1,s.state.longestStreak=Math.max(s.state.currentStreak,r.longestStreak),s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.streakMaintained=!0,s;s.daysMissed=c-1;let A=s.daysMissed;if(A<=r.freezesRemaining)return s.state.freezesRemaining=r.freezesRemaining-A,s.state.freezeUsedAt=t,s.state.currentStreak=r.currentStreak+1,s.state.longestStreak=Math.max(s.state.currentStreak,r.longestStreak),s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.freezeUsed=!0,s.streakMaintained=!0,s;return s.streakLost=!0,s.state.currentStreak=1,s.state.lastActivityAt=t,s.state.lastActivityDate=o,s.newStreak=!0,s}checkStatus(r,t=new Date){if(!r.lastActivityDate)return{isActive:!1,willExpireAt:null,canUseFreeze:!1,daysUntilExpiry:0};let o=this.getDateString(t),s=this.getDaysBetween(r.lastActivityDate,o);if(s===0){let A=this.addDays(t,1);return A.setHours(23,59,59,999),{isActive:!0,willExpireAt:A,canUseFreeze:r.freezesRemaining>0,daysUntilExpiry:1}}if(s===1){let A=new Date(t);return A.setHours(23+this.config.gracePeriodHours,59,59,999),{isActive:!0,willExpireAt:A,canUseFreeze:r.freezesRemaining>0,daysUntilExpiry:0}}let c=s-1;return{isActive:c<=r.freezesRemaining,willExpireAt:null,canUseFreeze:c<=r.freezesRemaining,daysUntilExpiry:-c}}useFreeze(r,t=new Date){if(r.freezesRemaining<=0)return null;return{...r,freezesRemaining:r.freezesRemaining-1,freezeUsedAt:t}}awardMonthlyFreezes(r){return{...r,freezesRemaining:Math.min(r.freezesRemaining+this.config.freezesPerMonth,this.config.maxFreezes)}}getInitialState(){return{currentStreak:0,longestStreak:0,lastActivityAt:null,lastActivityDate:null,freezesRemaining:this.config.freezesPerMonth,freezeUsedAt:null}}getMilestones(r){let t=[3,7,14,30,60,90,180,365,500,1000],o=t.filter((c)=>r>=c),s=t.find((c)=>r<c)??null;return{achieved:o,next:s}}getDateString(r){let t=r.getFullYear(),o=String(r.getMonth()+1).padStart(2,"0"),s=String(r.getDate()).padStart(2,"0");return`${t}-${o}-${s}`}getDaysBetween(r,t){let o=new Date(r),c=new Date(t).getTime()-o.getTime();return Math.floor(c/86400000)}addDays(r,t){return new Date(r.getTime()+t*24*60*60*1000)}}var Zo=new U;import{defineTranslation as Ct}from"@contractspec/lib.contracts-spec/translations";var Br=Ct({meta:{key:"learning-journey.messages",version:"1.0.0",domain:"learning-journey",description:"XP source labels for the learning-journey module",owners:["platform"],stability:"experimental"},locale:"en",fallback:"en",messages:{"xp.source.base":{value:"Base",description:"XP breakdown label for base XP"},"xp.source.scoreBonus":{value:"Score Bonus",description:"XP breakdown label for score-based bonus"},"xp.source.perfectScore":{value:"Perfect Score",description:"XP breakdown label for perfect score bonus"},"xp.source.firstAttempt":{value:"First Attempt",description:"XP breakdown label for first attempt bonus"},"xp.source.retryPenalty":{value:"Retry Penalty",description:"XP breakdown label for retry penalty"},"xp.source.streakBonus":{value:"Streak Bonus",description:"XP breakdown label for streak bonus"}}});import{defineTranslation as yt}from"@contractspec/lib.contracts-spec/translations";var jr=yt({meta:{key:"learning-journey.messages",version:"1.0.0",domain:"learning-journey",description:"XP source labels (Spanish)",owners:["platform"],stability:"experimental"},locale:"es",fallback:"en",messages:{"xp.source.base":{value:"Base",description:"XP breakdown label for base XP"},"xp.source.scoreBonus":{value:"Bonificación por puntuación",description:"XP breakdown label for score-based bonus"},"xp.source.perfectScore":{value:"Puntuación perfecta",description:"XP breakdown label for perfect score bonus"},"xp.source.firstAttempt":{value:"Primer intento",description:"XP breakdown label for first attempt bonus"},"xp.source.retryPenalty":{value:"Penalización por reintento",description:"XP breakdown label for retry penalty"},"xp.source.streakBonus":{value:"Bonificación por racha",description:"XP breakdown label for streak bonus"}}});import{defineTranslation as _t}from"@contractspec/lib.contracts-spec/translations";var ir=_t({meta:{key:"learning-journey.messages",version:"1.0.0",domain:"learning-journey",description:"XP source labels (French)",owners:["platform"],stability:"experimental"},locale:"fr",fallback:"en",messages:{"xp.source.base":{value:"Base",description:"XP breakdown label for base XP"},"xp.source.scoreBonus":{value:"Bonus de score",description:"XP breakdown label for score-based bonus"},"xp.source.perfectScore":{value:"Score parfait",description:"XP breakdown label for perfect score bonus"},"xp.source.firstAttempt":{value:"Premier essai",description:"XP breakdown label for first attempt bonus"},"xp.source.retryPenalty":{value:"Pénalité de réessai",description:"XP breakdown label for retry penalty"},"xp.source.streakBonus":{value:"Bonus de série",description:"XP breakdown label for streak bonus"}}});import{createI18nFactory as qt}from"@contractspec/lib.contracts-spec/translations";var tr=qt({specKey:"learning-journey.messages",catalogs:[Br,ir,jr]}),Kr=tr.create,Yo=tr.getDefault,Go=tr.resetRegistry;var w={baseValues:{lesson_complete:10,quiz_pass:20,quiz_perfect:50,flashcard_review:1,course_complete:200,module_complete:50,streak_bonus:5,achievement_unlock:0,daily_goal_complete:15,first_lesson:25,journey_step:5,journey_complete:50},scoreThresholds:[{min:90,multiplier:1.5},{min:80,multiplier:1.25},{min:70,multiplier:1},{min:60,multiplier:0.75},{min:0,multiplier:0.5}],streakTiers:[{days:365,bonus:50},{days:180,bonus:30},{days:90,bonus:20},{days:30,bonus:15},{days:14,bonus:10},{days:7,bonus:5},{days:3,bonus:2},{days:1,bonus:0}],perfectScoreMultiplier:1.5,firstAttemptBonus:10,retryPenalty:0.5,speedBonusMultiplier:1.2,speedBonusThreshold:0.8};class v{config;constructor(r={}){this.config={...w,...r,baseValues:{...w.baseValues,...r.baseValues},scoreThresholds:r.scoreThresholds||w.scoreThresholds,streakTiers:r.streakTiers||w.streakTiers}}calculate(r){let t=[],o=r.baseXp??this.config.baseValues[r.activity],s=o;if(t.push({source:"base",amount:o}),r.score!==void 0){let c=this.getScoreMultiplier(r.score);if(c!==1){let A=Math.round(o*(c-1));s+=A,t.push({source:"score_bonus",amount:A,multiplier:c})}if(r.score===100){let A=Math.round(o*(this.config.perfectScoreMultiplier-1));s+=A,t.push({source:"perfect_score",amount:A,multiplier:this.config.perfectScoreMultiplier})}}if(r.attemptNumber===1&&!r.isRetry)s+=this.config.firstAttemptBonus,t.push({source:"first_attempt",amount:this.config.firstAttemptBonus});if(r.isRetry){let c=Math.round(s*(1-this.config.retryPenalty));s-=c,t.push({source:"retry_penalty",amount:-c,multiplier:this.config.retryPenalty})}if(r.currentStreak&&r.currentStreak>0){let c=this.getStreakBonus(r.currentStreak);if(c>0)s+=c,t.push({source:"streak_bonus",amount:c})}if(o>0)s=Math.max(1,s);return{totalXp:Math.round(s),baseXp:o,breakdown:t}}calculateStreakBonus(r){let t=this.getStreakBonus(r);return{totalXp:t,baseXp:t,breakdown:[{source:"streak_bonus",amount:t}]}}getXpForLevel(r){if(r<=1)return 0;return Math.round(100*Math.pow(r-1,1.5))}getLevelFromXp(r){let t=1,o=this.getXpForLevel(t+1);while(r>=o&&t<1000)t++,o=this.getXpForLevel(t+1);let s=this.getXpForLevel(t),c=this.getXpForLevel(t+1);return{level:t,xpInLevel:r-s,xpForNextLevel:c-s}}getScoreMultiplier(r){for(let t of this.config.scoreThresholds)if(r>=t.min)return t.multiplier;return 1}getStreakBonus(r){for(let t of this.config.streakTiers)if(r>=t.days)return t.bonus;return 0}}var Ht={base:"xp.source.base",score_bonus:"xp.source.scoreBonus",perfect_score:"xp.source.perfectScore",first_attempt:"xp.source.firstAttempt",retry_penalty:"xp.source.retryPenalty",streak_bonus:"xp.source.streakBonus"};function No(r,t){let o=Kr(t),s=Ht[r];return s?o.t(s):r}var To=new v;import{defineEntity as z,defineEntityEnum as Cr,field as e,index as H}from"@contractspec/lib.schema";var yr=Cr({name:"LearningStyle",values:["VISUAL","AUDITORY","READING","KINESTHETIC","MIXED"],schema:"lssm_learning",description:"Preferred learning style."}),_r=Cr({name:"RecommendationType",values:["COURSE","LESSON","REVIEW","PRACTICE","ASSESSMENT","DECK"],schema:"lssm_learning",description:"Type of learning recommendation."}),xt=z({name:"LearnerProfile",description:"AI personalization profile for a learner.",schema:"lssm_learning",map:"learner_profile",fields:{id:e.id({description:"Unique profile identifier"}),learnerId:e.foreignKey({description:"Learner"}),learningStyle:e.enum("LearningStyle",{default:"MIXED",description:"Preferred learning style"}),preferredDifficulty:e.string({default:'"adaptive"',description:"Difficulty preference"}),preferredSessionLength:e.int({default:30,description:"Preferred session length in minutes"}),interests:e.json({isOptional:!0,description:"Topic interests"}),goals:e.json({isOptional:!0,description:"Learning goals"}),pacePreference:e.string({default:'"normal"',description:"Learning pace: slow, normal, fast"}),bestTimeOfDay:e.string({isOptional:!0,description:"Best time for learning"}),averageSessionLength:e.int({isOptional:!0,description:"Average session length"}),daysActivePerWeek:e.int({isOptional:!0,description:"Days active per week"}),avgQuizScore:e.int({isOptional:!0,description:"Average quiz score"}),avgLessonCompletionTime:e.int({isOptional:!0,description:"Avg lesson completion time"}),strengths:e.json({isOptional:!0,description:"Identified strengths"}),weaknesses:e.json({isOptional:!0,description:"Areas for improvement"}),lastAnalyzedAt:e.dateTime({isOptional:!0,description:"Last AI analysis"}),metadata:e.json({isOptional:!0,description:"Additional metadata"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.unique(["learnerId"],{name:"learner_profile_unique"}),H.on(["learningStyle"])],enums:[yr]}),Vt=z({name:"SkillMap",description:"Maps learner proficiency across skills.",schema:"lssm_learning",map:"skill_map",fields:{id:e.id({description:"Unique skill map identifier"}),learnerId:e.foreignKey({description:"Learner"}),skillId:e.string({description:"Skill identifier"}),skillName:e.string({description:"Skill name"}),skillCategory:e.string({isOptional:!0,description:"Skill category"}),level:e.int({default:0,description:"Proficiency level (0-100)"}),confidence:e.decimal({default:0.5,description:"Confidence in assessment"}),lessonsCompleted:e.int({default:0,description:"Related lessons completed"}),quizzesCompleted:e.int({default:0,description:"Related quizzes completed"}),practiceTime:e.int({default:0,description:"Practice time in minutes"}),lastPracticedAt:e.dateTime({isOptional:!0,description:"Last practice time"}),learningVelocity:e.decimal({isOptional:!0,description:"Learning speed for this skill"}),predictedTimeToMastery:e.int({isOptional:!0,description:"Predicted time to mastery (minutes)"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.unique(["learnerId","skillId"],{name:"skill_map_unique"}),H.on(["skillId","level"]),H.on(["learnerId","level"])]}),Lt=z({name:"LearningPath",description:"AI-generated personalized learning path.",schema:"lssm_learning",map:"learning_path",fields:{id:e.id({description:"Unique path identifier"}),learnerId:e.foreignKey({description:"Learner"}),name:e.string({description:"Path name"}),description:e.string({isOptional:!0,description:"Path description"}),goal:e.string({isOptional:!0,description:"Path goal"}),steps:e.json({description:"Ordered list of learning steps"}),currentStepIndex:e.int({default:0,description:"Current step index"}),progress:e.int({default:0,description:"Completion percentage"}),completedSteps:e.int({default:0,description:"Steps completed"}),totalSteps:e.int({default:0,description:"Total steps"}),generatedAt:e.dateTime({description:"When path was generated"}),adaptedFrom:e.string({isOptional:!0,description:"Original path ID if adapted"}),generationParams:e.json({isOptional:!0,description:"AI generation parameters"}),adaptationHistory:e.json({isOptional:!0,description:"Path adaptation history"}),isActive:e.boolean({default:!0,description:"Whether path is active"}),isCompleted:e.boolean({default:!1,description:"Whether path is completed"}),startedAt:e.dateTime({isOptional:!0,description:"When started"}),completedAt:e.dateTime({isOptional:!0,description:"When completed"}),estimatedCompletionDate:e.dateTime({isOptional:!0,description:"Estimated completion"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.on(["learnerId","isActive"]),H.on(["generatedAt"])]}),Zt=z({name:"Recommendation",description:"AI-powered learning recommendation.",schema:"lssm_learning",map:"recommendation",fields:{id:e.id({description:"Unique recommendation identifier"}),learnerId:e.foreignKey({description:"Learner"}),type:e.enum("RecommendationType",{description:"Recommendation type"}),itemId:e.string({description:"Recommended item ID"}),itemType:e.string({description:"Item type (course, lesson, deck, etc.)"}),score:e.decimal({description:"Recommendation score (0-1)"}),confidence:e.decimal({description:"Confidence in recommendation"}),reason:e.string({description:"Human-readable reason"}),factors:e.json({isOptional:!0,description:"Factors that contributed to recommendation"}),status:e.string({default:'"pending"',description:"Status: pending, viewed, accepted, dismissed"}),viewedAt:e.dateTime({isOptional:!0,description:"When viewed"}),acceptedAt:e.dateTime({isOptional:!0,description:"When accepted"}),dismissedAt:e.dateTime({isOptional:!0,description:"When dismissed"}),feedback:e.string({isOptional:!0,description:"User feedback"}),feedbackRating:e.int({isOptional:!0,description:"Feedback rating (1-5)"}),expiresAt:e.dateTime({isOptional:!0,description:"When recommendation expires"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.on(["learnerId","status","score"]),H.on(["type","status"]),H.on(["expiresAt"])],enums:[_r]}),$t=z({name:"LearningGap",description:"Identified learning gap.",schema:"lssm_learning",map:"learning_gap",fields:{id:e.id({description:"Unique gap identifier"}),learnerId:e.foreignKey({description:"Learner"}),skillId:e.string({description:"Skill with gap"}),skillName:e.string({description:"Skill name"}),severity:e.string({default:'"moderate"',description:"Gap severity: minor, moderate, major"}),confidence:e.decimal({description:"Confidence in gap detection"}),evidence:e.json({isOptional:!0,description:"Evidence for gap"}),relatedQuestions:e.json({isOptional:!0,description:"Questions that revealed gap"}),suggestedRemediation:e.json({isOptional:!0,description:"Suggested remediation"}),remediationProgress:e.int({default:0,description:"Remediation progress"}),status:e.string({default:'"open"',description:"Status: open, in_progress, resolved"}),resolvedAt:e.dateTime({isOptional:!0,description:"When resolved"}),detectedAt:e.dateTime({description:"When gap was detected"}),createdAt:e.createdAt(),updatedAt:e.updatedAt(),learner:e.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[H.on(["learnerId","status"]),H.on(["skillId","status"]),H.on(["severity","status"])]}),qr=[xt,Vt,Lt,Zt,$t],Hr=[yr,_r];import{defineEntity as Y,defineEntityEnum as G,field as b,index as Z}from"@contractspec/lib.schema";var xr=G({name:"CourseDifficulty",values:["BEGINNER","INTERMEDIATE","ADVANCED","EXPERT"],schema:"lssm_learning",description:"Difficulty level of a course."}),Vr=G({name:"CourseStatus",values:["DRAFT","PUBLISHED","ARCHIVED"],schema:"lssm_learning",description:"Publication status of a course."}),Lr=G({name:"LessonType",values:["CONTENT","VIDEO","INTERACTIVE","QUIZ","PRACTICE","PROJECT"],schema:"lssm_learning",description:"Type of lesson content."}),Zr=G({name:"ContentType",values:["MARKDOWN","VIDEO","AUDIO","EMBED","SCORM","CUSTOM"],schema:"lssm_learning",description:"Type of lesson content format."}),Mt=Y({name:"Course",description:"A structured learning course.",schema:"lssm_learning",map:"course",fields:{id:b.id({description:"Unique course identifier"}),title:b.string({description:"Course title"}),slug:b.string({isUnique:!0,description:"URL-friendly slug"}),description:b.string({isOptional:!0,description:"Course description"}),summary:b.string({isOptional:!0,description:"Short summary"}),difficulty:b.enum("CourseDifficulty",{default:"BEGINNER",description:"Difficulty level"}),category:b.string({isOptional:!0,description:"Course category"}),tags:b.json({isOptional:!0,description:"Tags for discovery"}),prerequisites:b.json({isOptional:!0,description:"Required course IDs"}),requiredSkills:b.json({isOptional:!0,description:"Required skill levels"}),estimatedDuration:b.int({isOptional:!0,description:"Estimated duration in minutes"}),thumbnailUrl:b.string({isOptional:!0,description:"Thumbnail image URL"}),coverImageUrl:b.string({isOptional:!0,description:"Cover image URL"}),promoVideoUrl:b.string({isOptional:!0,description:"Promo video URL"}),status:b.enum("CourseStatus",{default:"DRAFT",description:"Publication status"}),publishedAt:b.dateTime({isOptional:!0,description:"When published"}),authorId:b.string({description:"Author user ID"}),orgId:b.string({isOptional:!0,description:"Organization scope"}),isPublic:b.boolean({default:!1,description:"Whether course is publicly accessible"}),isFeatured:b.boolean({default:!1,description:"Whether course is featured"}),certificateEnabled:b.boolean({default:!1,description:"Award certificate on completion"}),metadata:b.json({isOptional:!0,description:"Additional metadata"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),modules:b.hasMany("CourseModule"),enrollments:b.hasMany("Enrollment")},indexes:[Z.on(["orgId","status"]),Z.on(["category"]),Z.on(["difficulty"]),Z.on(["authorId"])],enums:[xr,Vr]}),Wt=Y({name:"CourseModule",description:"A module (section) within a course.",schema:"lssm_learning",map:"course_module",fields:{id:b.id({description:"Unique module identifier"}),courseId:b.foreignKey({description:"Parent course"}),title:b.string({description:"Module title"}),description:b.string({isOptional:!0,description:"Module description"}),order:b.int({default:0,description:"Display order"}),unlockCondition:b.json({isOptional:!0,description:"Conditions to unlock module"}),prerequisiteModuleIds:b.json({isOptional:!0,description:"Required modules to complete first"}),estimatedDuration:b.int({isOptional:!0,description:"Estimated duration in minutes"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),course:b.belongsTo("Course",["courseId"],["id"],{onDelete:"Cascade"}),lessons:b.hasMany("Lesson"),completions:b.hasMany("ModuleCompletion")},indexes:[Z.on(["courseId","order"])]}),mt=Y({name:"Lesson",description:"An individual lesson within a module.",schema:"lssm_learning",map:"lesson",fields:{id:b.id({description:"Unique lesson identifier"}),moduleId:b.foreignKey({description:"Parent module"}),title:b.string({description:"Lesson title"}),description:b.string({isOptional:!0,description:"Lesson description"}),type:b.enum("LessonType",{default:"CONTENT",description:"Lesson type"}),order:b.int({default:0,description:"Display order"}),estimatedDuration:b.int({isOptional:!0,description:"Estimated duration in minutes"}),xpReward:b.int({default:10,description:"XP awarded on completion"}),isFree:b.boolean({default:!1,description:"Whether lesson is free preview"}),isRequired:b.boolean({default:!0,description:"Whether lesson is required for completion"}),metadata:b.json({isOptional:!0,description:"Additional metadata"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),module:b.belongsTo("CourseModule",["moduleId"],["id"],{onDelete:"Cascade"}),contents:b.hasMany("LessonContent"),progress:b.hasMany("LessonProgress"),quizzes:b.hasMany("Quiz")},indexes:[Z.on(["moduleId","order"]),Z.on(["type"])],enums:[Lr]}),Xt=Y({name:"LessonContent",description:"Content block within a lesson.",schema:"lssm_learning",map:"lesson_content",fields:{id:b.id({description:"Unique content identifier"}),lessonId:b.foreignKey({description:"Parent lesson"}),contentType:b.enum("ContentType",{description:"Content format"}),content:b.string({isOptional:!0,description:"Text content (markdown, etc.)"}),mediaUrl:b.string({isOptional:!0,description:"Media URL for video/audio"}),embedData:b.json({isOptional:!0,description:"Embed data for external content"}),order:b.int({default:0,description:"Display order"}),duration:b.int({isOptional:!0,description:"Content duration in seconds"}),metadata:b.json({isOptional:!0,description:"Additional metadata"}),createdAt:b.createdAt(),updatedAt:b.updatedAt(),lesson:b.belongsTo("Lesson",["lessonId"],["id"],{onDelete:"Cascade"})},indexes:[Z.on(["lessonId","order"])],enums:[Zr]}),$r=[Mt,Wt,mt,Xt],Mr=[xr,Vr,Lr,Zr];import{defineEntity as S,defineEntityEnum as zt,field as a,index as V}from"@contractspec/lib.schema";var Wr=zt({name:"CardRating",values:["AGAIN","HARD","GOOD","EASY"],schema:"lssm_learning",description:"Rating for a flashcard review."}),Ft=S({name:"Deck",description:"A collection of flashcards.",schema:"lssm_learning",map:"deck",fields:{id:a.id({description:"Unique deck identifier"}),ownerId:a.foreignKey({description:"Deck owner (learner)"}),title:a.string({description:"Deck title"}),description:a.string({isOptional:!0,description:"Deck description"}),category:a.string({isOptional:!0,description:"Deck category"}),tags:a.json({isOptional:!0,description:"Tags for discovery"}),isPublic:a.boolean({default:!1,description:"Whether deck is publicly visible"}),cardCount:a.int({default:0,description:"Number of cards"}),coverImageUrl:a.string({isOptional:!0,description:"Cover image URL"}),orgId:a.string({isOptional:!0,description:"Organization scope"}),newCardsPerDay:a.int({default:20,description:"New cards to introduce per day"}),reviewsPerDay:a.int({default:100,description:"Maximum reviews per day"}),metadata:a.json({isOptional:!0,description:"Additional metadata"}),createdAt:a.createdAt(),updatedAt:a.updatedAt(),owner:a.belongsTo("Learner",["ownerId"],["id"],{onDelete:"Cascade"}),cards:a.hasMany("Card")},indexes:[V.on(["ownerId"]),V.on(["isPublic","category"]),V.on(["orgId"])]}),Qt=S({name:"Card",description:"An individual flashcard.",schema:"lssm_learning",map:"card",fields:{id:a.id({description:"Unique card identifier"}),deckId:a.foreignKey({description:"Parent deck"}),front:a.string({description:"Front of card (question)"}),back:a.string({description:"Back of card (answer)"}),hints:a.json({isOptional:!0,description:"Hints for the card"}),explanation:a.string({isOptional:!0,description:"Detailed explanation"}),frontMediaUrl:a.string({isOptional:!0,description:"Media for front"}),backMediaUrl:a.string({isOptional:!0,description:"Media for back"}),audioUrl:a.string({isOptional:!0,description:"Audio pronunciation"}),tags:a.json({isOptional:!0,description:"Card tags"}),difficulty:a.int({default:0,description:"Card difficulty (0-5)"}),order:a.int({default:0,description:"Card order in deck"}),isSuspended:a.boolean({default:!1,description:"Whether card is suspended"}),metadata:a.json({isOptional:!0,description:"Additional metadata"}),createdAt:a.createdAt(),updatedAt:a.updatedAt(),deck:a.belongsTo("Deck",["deckId"],["id"],{onDelete:"Cascade"}),reviews:a.hasMany("CardReview"),schedules:a.hasMany("CardSchedule")},indexes:[V.on(["deckId","order"]),V.on(["isSuspended"])]}),Ut=S({name:"CardReview",description:"A single review of a flashcard.",schema:"lssm_learning",map:"card_review",fields:{id:a.id({description:"Unique review identifier"}),learnerId:a.foreignKey({description:"Reviewer"}),cardId:a.foreignKey({description:"Reviewed card"}),rating:a.enum("CardRating",{description:"Review rating"}),responseTimeMs:a.int({isOptional:!0,description:"Time to respond in ms"}),intervalBefore:a.int({description:"Interval before review (days)"}),easeFactorBefore:a.decimal({description:"Ease factor before review"}),intervalAfter:a.int({description:"Interval after review (days)"}),easeFactorAfter:a.decimal({description:"Ease factor after review"}),reviewType:a.string({default:'"review"',description:"Type: new, learning, review, relearning"}),reviewedAt:a.dateTime({description:"When reviewed"}),createdAt:a.createdAt(),learner:a.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),card:a.belongsTo("Card",["cardId"],["id"],{onDelete:"Cascade"})},indexes:[V.on(["learnerId","reviewedAt"]),V.on(["cardId","reviewedAt"]),V.on(["rating"])],enums:[Wr]}),wt=S({name:"CardSchedule",description:"SRS schedule for a learner/card pair.",schema:"lssm_learning",map:"card_schedule",fields:{id:a.id({description:"Unique schedule identifier"}),learnerId:a.foreignKey({description:"Learner"}),cardId:a.foreignKey({description:"Card"}),interval:a.int({default:0,description:"Current interval in days"}),easeFactor:a.decimal({default:2.5,description:"Ease factor (SM-2)"}),repetitions:a.int({default:0,description:"Number of successful repetitions"}),nextReviewAt:a.dateTime({description:"When next review is due"}),lastReviewAt:a.dateTime({isOptional:!0,description:"When last reviewed"}),learningStep:a.int({default:0,description:"Current learning step"}),isGraduated:a.boolean({default:!1,description:"Whether card has graduated"}),isRelearning:a.boolean({default:!1,description:"Whether card is being relearned"}),lapses:a.int({default:0,description:"Number of times card was forgotten"}),reviewCount:a.int({default:0,description:"Total review count"}),createdAt:a.createdAt(),updatedAt:a.updatedAt(),learner:a.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),card:a.belongsTo("Card",["cardId"],["id"],{onDelete:"Cascade"})},indexes:[V.unique(["learnerId","cardId"],{name:"card_schedule_unique"}),V.on(["learnerId","nextReviewAt"]),V.on(["nextReviewAt"])]}),mr=[Ft,Qt,Ut,wt],Xr=[Wr];import{defineEntity as W,defineEntityEnum as zr,field as n,index as i}from"@contractspec/lib.schema";var Fr=zr({name:"AchievementType",values:["MILESTONE","STREAK","SKILL","SOCIAL","SPECIAL","SEASONAL"],schema:"lssm_learning",description:"Type of achievement."}),Qr=zr({name:"LeaderboardPeriod",values:["DAILY","WEEKLY","MONTHLY","ALL_TIME"],schema:"lssm_learning",description:"Leaderboard time period."}),vt=W({name:"Achievement",description:"An achievement that can be unlocked.",schema:"lssm_learning",map:"achievement",fields:{id:n.id({description:"Unique achievement identifier"}),key:n.string({isUnique:!0,description:"Achievement key"}),name:n.string({description:"Achievement name"}),description:n.string({description:"Achievement description"}),icon:n.string({isOptional:!0,description:"Icon name or URL"}),color:n.string({isOptional:!0,description:"Display color"}),badgeUrl:n.string({isOptional:!0,description:"Badge image URL"}),type:n.enum("AchievementType",{default:"MILESTONE",description:"Achievement type"}),category:n.string({isOptional:!0,description:"Achievement category"}),rarity:n.string({default:'"common"',description:"Rarity: common, rare, epic, legendary"}),xpReward:n.int({default:50,description:"XP awarded"}),condition:n.json({description:"Unlock condition"}),order:n.int({default:0,description:"Display order"}),isHidden:n.boolean({default:!1,description:"Hide until unlocked"}),isActive:n.boolean({default:!0,description:"Whether achievement is active"}),orgId:n.string({isOptional:!0,description:"Organization scope"}),metadata:n.json({isOptional:!0,description:"Additional metadata"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learnerAchievements:n.hasMany("LearnerAchievement")},indexes:[i.on(["type"]),i.on(["category"]),i.on(["isActive"]),i.on(["orgId"])],enums:[Fr]}),Yt=W({name:"LearnerAchievement",description:"An achievement unlocked by a learner.",schema:"lssm_learning",map:"learner_achievement",fields:{id:n.id({description:"Unique record identifier"}),learnerId:n.foreignKey({description:"Learner"}),achievementId:n.foreignKey({description:"Achievement"}),xpEarned:n.int({default:0,description:"XP earned"}),progress:n.int({default:100,description:"Progress percentage"}),currentValue:n.int({isOptional:!0,description:"Current value towards goal"}),targetValue:n.int({isOptional:!0,description:"Target value for completion"}),unlockedAt:n.dateTime({description:"When unlocked"}),createdAt:n.createdAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),achievement:n.belongsTo("Achievement",["achievementId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId","achievementId"],{name:"learner_achievement_unique"}),i.on(["learnerId","unlockedAt"]),i.on(["achievementId"])]}),Gt=W({name:"Streak",description:"Tracks daily learning streaks.",schema:"lssm_learning",map:"streak",fields:{id:n.id({description:"Unique streak identifier"}),learnerId:n.foreignKey({description:"Learner"}),currentStreak:n.int({default:0,description:"Current streak days"}),longestStreak:n.int({default:0,description:"Longest streak ever"}),lastActivityAt:n.dateTime({isOptional:!0,description:"Last learning activity"}),lastActivityDate:n.string({isOptional:!0,description:"Last activity date (YYYY-MM-DD)"}),freezesRemaining:n.int({default:0,description:"Streak freezes available"}),freezeUsedAt:n.dateTime({isOptional:!0,description:"When last freeze was used"}),streakHistory:n.json({isOptional:!0,description:"Historical streak data"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId"],{name:"streak_learner_unique"}),i.on(["currentStreak"]),i.on(["longestStreak"])]}),St=W({name:"DailyGoal",description:"Daily XP goal tracking.",schema:"lssm_learning",map:"daily_goal",fields:{id:n.id({description:"Unique goal identifier"}),learnerId:n.foreignKey({description:"Learner"}),date:n.string({description:"Date (YYYY-MM-DD)"}),targetXp:n.int({description:"Target XP for the day"}),currentXp:n.int({default:0,description:"XP earned today"}),isCompleted:n.boolean({default:!1,description:"Whether goal was met"}),completedAt:n.dateTime({isOptional:!0,description:"When goal was completed"}),xpBreakdown:n.json({isOptional:!0,description:"XP sources breakdown"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId","date"],{name:"daily_goal_unique"}),i.on(["date","isCompleted"])]}),Rt=W({name:"LeaderboardEntry",description:"Leaderboard entry for a learner.",schema:"lssm_learning",map:"leaderboard_entry",fields:{id:n.id({description:"Unique entry identifier"}),learnerId:n.foreignKey({description:"Learner"}),periodType:n.enum("LeaderboardPeriod",{description:"Period type"}),periodKey:n.string({description:"Period key (e.g., 2024-W01)"}),xp:n.int({default:0,description:"XP earned in period"}),rank:n.int({isOptional:!0,description:"Rank in leaderboard"}),lessonsCompleted:n.int({default:0,description:"Lessons completed"}),quizzesPassed:n.int({default:0,description:"Quizzes passed"}),cardsReviewed:n.int({default:0,description:"Cards reviewed"}),streakDays:n.int({default:0,description:"Streak days in period"}),league:n.string({isOptional:!0,description:"League tier"}),orgId:n.string({isOptional:!0,description:"Organization scope"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId","periodType","periodKey"],{name:"leaderboard_entry_unique"}),i.on(["periodType","periodKey","xp"]),i.on(["orgId","periodType","periodKey","xp"])],enums:[Qr]}),Nt=W({name:"Heart",description:"Lives/hearts system for quiz attempts.",schema:"lssm_learning",map:"heart",fields:{id:n.id({description:"Unique heart record identifier"}),learnerId:n.foreignKey({description:"Learner"}),current:n.int({default:5,description:"Current hearts"}),max:n.int({default:5,description:"Maximum hearts"}),lastRefillAt:n.dateTime({isOptional:!0,description:"Last refill time"}),nextRefillAt:n.dateTime({isOptional:!0,description:"Next refill time"}),refillIntervalMinutes:n.int({default:240,description:"Minutes between refills"}),infiniteUntil:n.dateTime({isOptional:!0,description:"Infinite hearts until"}),createdAt:n.createdAt(),updatedAt:n.updatedAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.unique(["learnerId"],{name:"heart_learner_unique"}),i.on(["nextRefillAt"])]}),Tt=W({name:"XPTransaction",description:"Record of XP earned or spent.",schema:"lssm_learning",map:"xp_transaction",fields:{id:n.id({description:"Unique transaction identifier"}),learnerId:n.foreignKey({description:"Learner"}),amount:n.int({description:"XP amount (positive = earned, negative = spent)"}),type:n.string({description:"Transaction type (lesson, quiz, streak, achievement, etc.)"}),sourceType:n.string({isOptional:!0,description:"Source entity type"}),sourceId:n.string({isOptional:!0,description:"Source entity ID"}),description:n.string({isOptional:!0,description:"Human-readable description"}),balanceAfter:n.int({description:"Total XP after transaction"}),createdAt:n.createdAt(),learner:n.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[i.on(["learnerId","createdAt"]),i.on(["type"]),i.on(["sourceType","sourceId"])]}),Ur=[vt,Yt,Gt,St,Rt,Nt,Tt],wr=[Fr,Qr];import{defineEntity as F,defineEntityEnum as vr,field as p,index as _}from"@contractspec/lib.schema";var Yr=vr({name:"EnrollmentStatus",values:["ENROLLED","IN_PROGRESS","COMPLETED","DROPPED","EXPIRED"],schema:"lssm_learning",description:"Status of a course enrollment."}),Gr=vr({name:"ProgressStatus",values:["NOT_STARTED","IN_PROGRESS","COMPLETED","SKIPPED"],schema:"lssm_learning",description:"Status of lesson progress."}),dt=F({name:"Learner",description:"A learner profile.",schema:"lssm_learning",map:"learner",fields:{id:p.id({description:"Unique learner identifier"}),userId:p.string({isUnique:!0,description:"Associated user ID"}),displayName:p.string({isOptional:!0,description:"Display name"}),avatarUrl:p.string({isOptional:!0,description:"Avatar URL"}),bio:p.string({isOptional:!0,description:"Short bio"}),level:p.int({default:1,description:"Current level"}),totalXp:p.int({default:0,description:"Total XP earned"}),currentStreak:p.int({default:0,description:"Current streak days"}),longestStreak:p.int({default:0,description:"Longest streak ever"}),lastActivityAt:p.dateTime({isOptional:!0,description:"Last learning activity"}),locale:p.string({isOptional:!0,description:'Preferred locale for learning content (e.g. "en", "fr", "es")'}),timezone:p.string({default:'"UTC"',description:"Learner timezone"}),dailyGoalXp:p.int({default:50,description:"Daily XP goal"}),reminderEnabled:p.boolean({default:!0,description:"Enable reminders"}),reminderTime:p.string({isOptional:!0,description:"Preferred reminder time"}),orgId:p.string({isOptional:!0,description:"Organization scope"}),metadata:p.json({isOptional:!0,description:"Additional metadata"}),createdAt:p.createdAt(),updatedAt:p.updatedAt(),enrollments:p.hasMany("Enrollment"),lessonProgress:p.hasMany("LessonProgress"),achievements:p.hasMany("LearnerAchievement"),decks:p.hasMany("Deck"),profile:p.hasOne("LearnerProfile")},indexes:[_.on(["orgId"]),_.on(["totalXp"]),_.on(["level"]),_.on(["currentStreak"])]}),Et=F({name:"Enrollment",description:"A learner enrollment in a course.",schema:"lssm_learning",map:"enrollment",fields:{id:p.id({description:"Unique enrollment identifier"}),learnerId:p.foreignKey({description:"Enrolled learner"}),courseId:p.foreignKey({description:"Enrolled course"}),status:p.enum("EnrollmentStatus",{default:"ENROLLED",description:"Enrollment status"}),progress:p.int({default:0,description:"Completion percentage (0-100)"}),completedLessons:p.int({default:0,description:"Number of completed lessons"}),totalLessons:p.int({default:0,description:"Total lessons in course"}),xpEarned:p.int({default:0,description:"XP earned in this course"}),startedAt:p.dateTime({isOptional:!0,description:"When learner started"}),completedAt:p.dateTime({isOptional:!0,description:"When learner completed"}),lastAccessedAt:p.dateTime({isOptional:!0,description:"Last access time"}),certificateId:p.string({isOptional:!0,description:"Issued certificate ID"}),metadata:p.json({isOptional:!0,description:"Additional metadata"}),createdAt:p.createdAt(),updatedAt:p.updatedAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),course:p.belongsTo("Course",["courseId"],["id"],{onDelete:"Cascade"})},indexes:[_.unique(["learnerId","courseId"],{name:"enrollment_unique"}),_.on(["learnerId","status"]),_.on(["courseId","status"])],enums:[Yr]}),ft=F({name:"LessonProgress",description:"Progress tracking for a lesson.",schema:"lssm_learning",map:"lesson_progress",fields:{id:p.id({description:"Unique progress identifier"}),learnerId:p.foreignKey({description:"Learner"}),lessonId:p.foreignKey({description:"Lesson"}),status:p.enum("ProgressStatus",{default:"NOT_STARTED",description:"Progress status"}),progress:p.int({default:0,description:"Completion percentage (0-100)"}),score:p.int({isOptional:!0,description:"Score achieved (for quizzes)"}),attempts:p.int({default:0,description:"Number of attempts"}),bestScore:p.int({isOptional:!0,description:"Best score across attempts"}),timeSpent:p.int({default:0,description:"Time spent in seconds"}),xpEarned:p.int({default:0,description:"XP earned from this lesson"}),startedAt:p.dateTime({isOptional:!0,description:"When started"}),completedAt:p.dateTime({isOptional:!0,description:"When completed"}),lastAccessedAt:p.dateTime({isOptional:!0,description:"Last access time"}),bookmarks:p.json({isOptional:!0,description:"Content bookmarks"}),notes:p.string({isOptional:!0,description:"Learner notes"}),createdAt:p.createdAt(),updatedAt:p.updatedAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),lesson:p.belongsTo("Lesson",["lessonId"],["id"],{onDelete:"Cascade"})},indexes:[_.unique(["learnerId","lessonId"],{name:"lesson_progress_unique"}),_.on(["learnerId","status"]),_.on(["lessonId"])],enums:[Gr]}),lt=F({name:"ModuleCompletion",description:"Module completion record.",schema:"lssm_learning",map:"module_completion",fields:{id:p.id({description:"Unique completion identifier"}),learnerId:p.foreignKey({description:"Learner"}),moduleId:p.foreignKey({description:"Module"}),score:p.int({isOptional:!0,description:"Average score"}),xpEarned:p.int({default:0,description:"XP earned"}),timeSpent:p.int({default:0,description:"Time spent in seconds"}),completedAt:p.dateTime({description:"When completed"}),createdAt:p.createdAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),module:p.belongsTo("CourseModule",["moduleId"],["id"],{onDelete:"Cascade"})},indexes:[_.unique(["learnerId","moduleId"],{name:"module_completion_unique"}),_.on(["learnerId","completedAt"])]}),rs=F({name:"Certificate",description:"Course completion certificate.",schema:"lssm_learning",map:"certificate",fields:{id:p.id({description:"Unique certificate identifier"}),learnerId:p.foreignKey({description:"Certificate holder"}),courseId:p.foreignKey({description:"Completed course"}),enrollmentId:p.foreignKey({description:"Associated enrollment"}),certificateNumber:p.string({isUnique:!0,description:"Unique certificate number"}),title:p.string({description:"Certificate title"}),description:p.string({isOptional:!0,description:"Certificate description"}),score:p.int({isOptional:!0,description:"Final score"}),grade:p.string({isOptional:!0,description:"Grade awarded"}),issuedAt:p.dateTime({description:"When issued"}),validUntil:p.dateTime({isOptional:!0,description:"Expiration date"}),verificationUrl:p.string({isOptional:!0,description:"Verification URL"}),credentialHash:p.string({isOptional:!0,description:"Credential hash for verification"}),isRevoked:p.boolean({default:!1,description:"Whether certificate is revoked"}),revokedAt:p.dateTime({isOptional:!0,description:"When revoked"}),revokedReason:p.string({isOptional:!0,description:"Revocation reason"}),metadata:p.json({isOptional:!0,description:"Additional metadata"}),createdAt:p.createdAt(),learner:p.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),course:p.belongsTo("Course",["courseId"],["id"],{onDelete:"Cascade"})},indexes:[_.on(["learnerId"]),_.on(["courseId"]),_.on(["issuedAt"])]}),Sr=[dt,Et,ft,lt,rs],Rr=[Yr,Gr];import{defineEntity as R,defineEntityEnum as ts,field as J,index as $}from"@contractspec/lib.schema";var ss=ts({name:"JourneyStepStatus",values:["LOCKED","AVAILABLE","COMPLETED","MISSED","SKIPPED","BLOCKED"],schema:"lssm_learning",description:"Adaptive runtime status of a journey step."}),os=R({name:"JourneyTrack",description:"Adaptive journey track definition.",schema:"lssm_learning",map:"journey_track",fields:{id:J.id({description:"Unique track identifier"}),productId:J.string({isOptional:!0,description:"Product scope"}),name:J.string({description:"Track name"}),description:J.string({isOptional:!0,description:"Track description"}),targetUserSegment:J.string({isOptional:!0,description:"Target learner segment"}),targetRole:J.string({isOptional:!0,description:"Target learner role"}),isActive:J.boolean({default:!0,description:"Whether the track is active"}),isRequired:J.boolean({default:!1,description:"Whether the track must be completed"}),canSkip:J.boolean({default:!1,description:"Whether steps can be skipped"}),totalXp:J.int({default:0,description:"Declared XP ceiling for the track"}),streakRule:J.json({isOptional:!0,description:"Tempo rule used for streak-based rewards"}),completionRewards:J.json({isOptional:!0,description:"Reward applied when the track is completed"}),metadata:J.json({isOptional:!0,description:"Additional track metadata"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),steps:J.hasMany("JourneyStep"),progress:J.hasMany("JourneyProgress")},indexes:[$.on(["productId","isActive"]),$.unique(["productId","targetUserSegment","targetRole"],{name:"journey_track_target"})]}),es=R({name:"JourneyStep",description:"Adaptive journey step definition.",schema:"lssm_learning",map:"journey_step",fields:{id:J.id({description:"Unique step identifier"}),trackId:J.foreignKey({description:"Parent journey track"}),title:J.string({description:"Step title"}),description:J.string({isOptional:!0,description:"Step description"}),instructions:J.string({isOptional:!0,description:"How the learner should complete the step"}),helpUrl:J.string({isOptional:!0,description:"Help or destination URL"}),order:J.int({default:0,description:"Display order"}),completion:J.json({description:"Structured completion condition for this step"}),availability:J.json({isOptional:!0,description:"Unlock and due-window configuration"}),prerequisites:J.json({isOptional:!0,description:"Prerequisites that must be satisfied to unlock the step"}),prerequisiteMode:J.string({isOptional:!0,description:"How to combine prerequisites (all|any)"}),branches:J.json({isOptional:!0,description:"Branch outcomes evaluated when the step completes"}),reward:J.json({isOptional:!0,description:"Reward granted by this step"}),xpReward:J.int({isOptional:!0,description:"Legacy XP reward field"}),isRequired:J.boolean({default:!0,description:"Whether the step is required"}),canSkip:J.boolean({default:!1,description:"Whether the step can be skipped"}),actionUrl:J.string({isOptional:!0,description:"Manual completion action URL"}),actionLabel:J.string({isOptional:!0,description:"Manual action label"}),metadata:J.json({isOptional:!0,description:"Additional step metadata"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),track:J.belongsTo("JourneyTrack",["trackId"],["id"],{onDelete:"Cascade"})},indexes:[$.on(["trackId","order"])]}),ns=R({name:"JourneyProgress",description:"Persisted learner progress through an adaptive journey.",schema:"lssm_learning",map:"journey_progress",fields:{id:J.id({description:"Unique progress identifier"}),learnerId:J.foreignKey({description:"Learner owning this journey state"}),trackId:J.foreignKey({description:"Journey track"}),progressPercent:J.int({default:0,description:"Completion percentage"}),isCompleted:J.boolean({default:!1,description:"Whether the track is completed"}),nextStepId:J.string({isOptional:!0,description:"Next actionable step projected by the engine"}),xpEarned:J.int({default:0,description:"Total XP earned so far"}),badges:J.json({default:"[]",description:"Unlocked badge keys"}),streakState:J.json({default:"{}",description:"Serialized streak engine state"}),eventLog:J.json({default:"[]",description:"Recorded events used to evaluate adaptive progress"}),completionRewardApplied:J.boolean({default:!1,description:"Whether track completion rewards were already applied"}),startedAt:J.dateTime({isOptional:!0,description:"Track start timestamp"}),completedAt:J.dateTime({isOptional:!0,description:"Track completion timestamp"}),lastActivityAt:J.dateTime({isOptional:!0,description:"Latest event or manual advancement timestamp"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),learner:J.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),track:J.belongsTo("JourneyTrack",["trackId"],["id"],{onDelete:"Cascade"}),stepProgress:J.hasMany("JourneyStepProgress")},indexes:[$.unique(["learnerId","trackId"],{name:"journey_progress_unique"}),$.on(["learnerId","isCompleted"]),$.on(["trackId"])]}),ps=R({name:"JourneyStepProgress",description:"Persisted step-level state for an adaptive journey.",schema:"lssm_learning",map:"journey_step_progress",fields:{id:J.id({description:"Unique step-progress identifier"}),progressId:J.foreignKey({description:"Parent journey progress record"}),stepId:J.foreignKey({description:"Referenced journey step"}),status:J.enum("JourneyStepStatus",{description:"Current step status"}),selectedBranchKey:J.string({isOptional:!0,description:"Selected branch outcome for this step"}),xpEarned:J.int({default:0,description:"XP earned from this step"}),occurrences:J.int({default:0,description:"Matching event occurrences"}),masteryCount:J.int({default:0,description:"Accumulated mastery hits"}),availableAt:J.dateTime({isOptional:!0,description:"When the step unlocked"}),dueAt:J.dateTime({isOptional:!0,description:"When the step expires"}),completedAt:J.dateTime({isOptional:!0,description:"When the step completed"}),skippedAt:J.dateTime({isOptional:!0,description:"When the step was skipped"}),blockedAt:J.dateTime({isOptional:!0,description:"When the step was blocked"}),missedAt:J.dateTime({isOptional:!0,description:"When the step was missed"}),triggeringEvent:J.string({isOptional:!0,description:"Event that completed the step"}),eventPayload:J.json({isOptional:!0,description:"Event payload snapshot"}),createdAt:J.createdAt(),updatedAt:J.updatedAt(),progress:J.belongsTo("JourneyProgress",["progressId"],["id"],{onDelete:"Cascade"}),step:J.belongsTo("JourneyStep",["stepId"],["id"],{onDelete:"Cascade"})},indexes:[$.unique(["progressId","stepId"],{name:"journey_step_progress_unique"}),$.on(["status"])]}),Nr=[os,es,ns,ps],Tr=[ss];import{defineEntity as Q,defineEntityEnum as sr,field as g,index as x}from"@contractspec/lib.schema";var dr=sr({name:"QuestionType",values:["MULTIPLE_CHOICE","TRUE_FALSE","FILL_BLANK","MATCHING","SHORT_ANSWER","CODE"],schema:"lssm_learning",description:"Type of quiz question."}),Er=sr({name:"QuizStatus",values:["DRAFT","PUBLISHED","ARCHIVED"],schema:"lssm_learning",description:"Publication status of a quiz."}),fr=sr({name:"AttemptStatus",values:["IN_PROGRESS","COMPLETED","TIMED_OUT","ABANDONED"],schema:"lssm_learning",description:"Status of a quiz attempt."}),us=Q({name:"Quiz",description:"A quiz assessment.",schema:"lssm_learning",map:"quiz",fields:{id:g.id({description:"Unique quiz identifier"}),lessonId:g.foreignKey({isOptional:!0,description:"Associated lesson"}),title:g.string({description:"Quiz title"}),description:g.string({isOptional:!0,description:"Quiz description"}),instructions:g.string({isOptional:!0,description:"Quiz instructions"}),passingScore:g.int({default:70,description:"Passing score percentage"}),timeLimit:g.int({isOptional:!0,description:"Time limit in seconds"}),maxAttempts:g.int({isOptional:!0,description:"Maximum attempts allowed"}),shuffleQuestions:g.boolean({default:!1,description:"Shuffle question order"}),shuffleOptions:g.boolean({default:!1,description:"Shuffle answer options"}),showCorrectAnswers:g.boolean({default:!0,description:"Show correct answers after"}),showExplanations:g.boolean({default:!0,description:"Show explanations after"}),status:g.enum("QuizStatus",{default:"DRAFT",description:"Publication status"}),totalPoints:g.int({default:0,description:"Total points available"}),xpReward:g.int({default:20,description:"XP for passing"}),orgId:g.string({isOptional:!0,description:"Organization scope"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),lesson:g.belongsTo("Lesson",["lessonId"],["id"],{onDelete:"Cascade"}),questions:g.hasMany("Question"),attempts:g.hasMany("QuizAttempt")},indexes:[x.on(["lessonId"]),x.on(["status"]),x.on(["orgId"])],enums:[Er]}),cs=Q({name:"Question",description:"A quiz question.",schema:"lssm_learning",map:"question",fields:{id:g.id({description:"Unique question identifier"}),quizId:g.foreignKey({description:"Parent quiz"}),type:g.enum("QuestionType",{description:"Question type"}),content:g.string({description:"Question text"}),mediaUrl:g.string({isOptional:!0,description:"Question media"}),points:g.int({default:1,description:"Points for correct answer"}),codeLanguage:g.string({isOptional:!0,description:"Programming language"}),codeTemplate:g.string({isOptional:!0,description:"Starter code template"}),testCases:g.json({isOptional:!0,description:"Test cases for code validation"}),explanation:g.string({isOptional:!0,description:"Explanation of correct answer"}),hint:g.string({isOptional:!0,description:"Hint for the question"}),order:g.int({default:0,description:"Display order"}),skillId:g.string({isOptional:!0,description:"Associated skill"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),quiz:g.belongsTo("Quiz",["quizId"],["id"],{onDelete:"Cascade"}),options:g.hasMany("QuestionOption")},indexes:[x.on(["quizId","order"]),x.on(["type"]),x.on(["skillId"])],enums:[dr]}),gs=Q({name:"QuestionOption",description:"An answer option for a question.",schema:"lssm_learning",map:"question_option",fields:{id:g.id({description:"Unique option identifier"}),questionId:g.foreignKey({description:"Parent question"}),content:g.string({description:"Option text"}),matchContent:g.string({isOptional:!0,description:"Match pair content"}),isCorrect:g.boolean({default:!1,description:"Whether option is correct"}),feedback:g.string({isOptional:!0,description:"Feedback when selected"}),order:g.int({default:0,description:"Display order"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),question:g.belongsTo("Question",["questionId"],["id"],{onDelete:"Cascade"})},indexes:[x.on(["questionId","order"])]}),ks=Q({name:"QuizAttempt",description:"A learner quiz attempt.",schema:"lssm_learning",map:"quiz_attempt",fields:{id:g.id({description:"Unique attempt identifier"}),learnerId:g.foreignKey({description:"Learner"}),quizId:g.foreignKey({description:"Quiz"}),status:g.enum("AttemptStatus",{default:"IN_PROGRESS",description:"Attempt status"}),score:g.int({isOptional:!0,description:"Score achieved"}),percentageScore:g.int({isOptional:!0,description:"Percentage score"}),passed:g.boolean({isOptional:!0,description:"Whether passed"}),totalQuestions:g.int({default:0,description:"Total questions"}),answeredQuestions:g.int({default:0,description:"Questions answered"}),correctAnswers:g.int({default:0,description:"Correct answers"}),answers:g.json({isOptional:!0,description:"Submitted answers"}),xpEarned:g.int({default:0,description:"XP earned"}),timeSpent:g.int({default:0,description:"Time spent in seconds"}),startedAt:g.dateTime({description:"When started"}),completedAt:g.dateTime({isOptional:!0,description:"When completed"}),attemptNumber:g.int({default:1,description:"Which attempt this is"}),metadata:g.json({isOptional:!0,description:"Additional metadata"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),learner:g.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"}),quiz:g.belongsTo("Quiz",["quizId"],["id"],{onDelete:"Cascade"})},indexes:[x.on(["learnerId","quizId"]),x.on(["learnerId","status"]),x.on(["quizId","status"])],enums:[fr]}),Js=Q({name:"SkillAssessment",description:"Assessment of a skill based on quiz performance.",schema:"lssm_learning",map:"skill_assessment",fields:{id:g.id({description:"Unique assessment identifier"}),learnerId:g.foreignKey({description:"Learner"}),skillId:g.string({description:"Skill identifier"}),skillName:g.string({description:"Skill name"}),level:g.int({default:1,description:"Proficiency level (1-5)"}),score:g.int({default:0,description:"Assessment score (0-100)"}),confidence:g.decimal({default:0.5,description:"Confidence in assessment"}),questionsAnswered:g.int({default:0,description:"Total questions answered"}),questionsCorrect:g.int({default:0,description:"Total correct"}),assessedAt:g.dateTime({description:"Last assessment time"}),createdAt:g.createdAt(),updatedAt:g.updatedAt(),learner:g.belongsTo("Learner",["learnerId"],["id"],{onDelete:"Cascade"})},indexes:[x.unique(["learnerId","skillId"],{name:"skill_assessment_unique"}),x.on(["learnerId","level"]),x.on(["skillId"])]}),lr=[us,cs,gs,ks,Js],rt=[dr,Er,fr];var hs=[...$r,...Sr,...Nr,...mr,...lr,...Ur,...qr],bs=[...Mr,...Rr,...Tr,...Xr,...rt,...wr,...Hr],Ke={moduleId:"@contractspec/module.learning-journey",entities:hs,enums:bs};import{defineEvent as K}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as C,ScalarTypeEnum as u}from"@contractspec/lib.schema";var as=C({name:"CoursePublishedEventPayload",description:"Payload when a course is published",fields:{courseId:{type:u.String_unsecure(),isOptional:!1},title:{type:u.String_unsecure(),isOptional:!1},authorId:{type:u.String_unsecure(),isOptional:!1},publishedAt:{type:u.DateTime(),isOptional:!1}}}),Is=K({meta:{key:"course.published",version:"1.0.0",description:"A course has been published.",stability:"stable",owners:[...P],tags:["learning","course"]},payload:as}),As=C({name:"EnrollmentCreatedEventPayload",description:"Payload when a learner enrolls in a course",fields:{enrollmentId:{type:u.String_unsecure(),isOptional:!1},learnerId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},enrolledAt:{type:u.DateTime(),isOptional:!1}}}),Os=K({meta:{key:"enrollment.created",version:"1.0.0",description:"A learner has enrolled in a course.",stability:"stable",owners:[...P],tags:["learning","enrollment"]},payload:As}),Ps=C({name:"LessonCompletedEventPayload",description:"Payload when a lesson is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},lessonId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},score:{type:u.Int_unsecure(),isOptional:!0},xpEarned:{type:u.Int_unsecure(),isOptional:!1},timeSpent:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),Ds=K({meta:{key:"lesson.completed",version:"1.0.0",description:"A learner has completed a lesson.",stability:"stable",owners:[...P],tags:["learning","progress","lesson"]},payload:Ps}),Bs=C({name:"CourseCompletedEventPayload",description:"Payload when a course is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},enrollmentId:{type:u.String_unsecure(),isOptional:!1},score:{type:u.Int_unsecure(),isOptional:!0},xpEarned:{type:u.Int_unsecure(),isOptional:!1},certificateId:{type:u.String_unsecure(),isOptional:!0},completedAt:{type:u.DateTime(),isOptional:!1}}}),js=K({meta:{key:"course.completed",version:"1.0.0",description:"A learner has completed a course.",stability:"stable",owners:[...P],tags:["learning","progress","course"]},payload:Bs}),is=C({name:"OnboardingStartedEventPayload",description:"Payload when onboarding starts",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},trackId:{type:u.String_unsecure(),isOptional:!1},productId:{type:u.String_unsecure(),isOptional:!1},startedAt:{type:u.DateTime(),isOptional:!1}}}),Ks=K({meta:{key:"onboarding.started",version:"1.0.0",description:"A learner has started onboarding.",stability:"stable",owners:[...P],tags:["learning","onboarding"]},payload:is}),Cs=C({name:"OnboardingStepCompletedEventPayload",description:"Payload when an onboarding step is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},trackId:{type:u.String_unsecure(),isOptional:!1},stepId:{type:u.String_unsecure(),isOptional:!1},triggeringEvent:{type:u.String_unsecure(),isOptional:!0},xpEarned:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),ys=K({meta:{key:"onboarding.step_completed",version:"1.0.0",description:"An onboarding step has been completed.",stability:"stable",owners:[...P],tags:["learning","onboarding"]},payload:Cs}),_s=C({name:"OnboardingCompletedEventPayload",description:"Payload when onboarding is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},trackId:{type:u.String_unsecure(),isOptional:!1},productId:{type:u.String_unsecure(),isOptional:!1},xpEarned:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),qs=K({meta:{key:"onboarding.completed",version:"1.0.0",description:"A learner has completed onboarding.",stability:"stable",owners:[...P],tags:["learning","onboarding"]},payload:_s}),Hs=C({name:"CardReviewedEventPayload",description:"Payload when a flashcard is reviewed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},cardId:{type:u.String_unsecure(),isOptional:!1},deckId:{type:u.String_unsecure(),isOptional:!1},rating:{type:u.String_unsecure(),isOptional:!1},responseTimeMs:{type:u.Int_unsecure(),isOptional:!0},reviewedAt:{type:u.DateTime(),isOptional:!1}}}),xs=K({meta:{key:"flashcard.reviewed",version:"1.0.0",description:"A flashcard has been reviewed.",stability:"stable",owners:[...P],tags:["learning","flashcards"]},payload:Hs}),Vs=C({name:"QuizStartedEventPayload",description:"Payload when a quiz is started",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},quizId:{type:u.String_unsecure(),isOptional:!1},attemptId:{type:u.String_unsecure(),isOptional:!1},attemptNumber:{type:u.Int_unsecure(),isOptional:!1},startedAt:{type:u.DateTime(),isOptional:!1}}}),Ls=K({meta:{key:"quiz.started",version:"1.0.0",description:"A quiz attempt has started.",stability:"stable",owners:[...P],tags:["learning","quiz"]},payload:Vs}),Zs=C({name:"QuizCompletedEventPayload",description:"Payload when a quiz is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},quizId:{type:u.String_unsecure(),isOptional:!1},attemptId:{type:u.String_unsecure(),isOptional:!1},score:{type:u.Int_unsecure(),isOptional:!1},percentageScore:{type:u.Int_unsecure(),isOptional:!1},passed:{type:u.Boolean(),isOptional:!1},xpEarned:{type:u.Int_unsecure(),isOptional:!1},timeSpent:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),$s=K({meta:{key:"quiz.completed",version:"1.0.0",description:"A quiz attempt has been completed.",stability:"stable",owners:[...P],tags:["learning","quiz"]},payload:Zs}),Ms=C({name:"XpEarnedEventPayload",description:"Payload when XP is earned",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},amount:{type:u.Int_unsecure(),isOptional:!1},source:{type:u.String_unsecure(),isOptional:!1},sourceId:{type:u.String_unsecure(),isOptional:!0},totalXp:{type:u.Int_unsecure(),isOptional:!1},earnedAt:{type:u.DateTime(),isOptional:!1}}}),Ws=K({meta:{key:"xp.earned",version:"1.0.0",description:"XP has been earned.",stability:"stable",owners:[...P],tags:["learning","gamification","xp"]},payload:Ms}),ms=C({name:"LevelUpEventPayload",description:"Payload when a learner levels up",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},previousLevel:{type:u.Int_unsecure(),isOptional:!1},newLevel:{type:u.Int_unsecure(),isOptional:!1},totalXp:{type:u.Int_unsecure(),isOptional:!1},leveledUpAt:{type:u.DateTime(),isOptional:!1}}}),Xs=K({meta:{key:"level.up",version:"1.0.0",description:"A learner has leveled up.",stability:"stable",owners:[...P],tags:["learning","gamification","level"]},payload:ms}),zs=C({name:"StreakUpdatedEventPayload",description:"Payload when a streak is updated",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},previousStreak:{type:u.Int_unsecure(),isOptional:!1},currentStreak:{type:u.Int_unsecure(),isOptional:!1},longestStreak:{type:u.Int_unsecure(),isOptional:!1},freezeUsed:{type:u.Boolean(),isOptional:!1},updatedAt:{type:u.DateTime(),isOptional:!1}}}),Fs=K({meta:{key:"streak.updated",version:"1.0.0",description:"A streak has been updated.",stability:"stable",owners:[...P],tags:["learning","gamification","streak"]},payload:zs}),Qs=C({name:"AchievementUnlockedEventPayload",description:"Payload when an achievement is unlocked",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},achievementId:{type:u.String_unsecure(),isOptional:!1},achievementKey:{type:u.String_unsecure(),isOptional:!1},achievementName:{type:u.String_unsecure(),isOptional:!1},xpEarned:{type:u.Int_unsecure(),isOptional:!1},unlockedAt:{type:u.DateTime(),isOptional:!1}}}),Us=K({meta:{key:"achievement.unlocked",version:"1.0.0",description:"An achievement has been unlocked.",stability:"stable",owners:[...P],tags:["learning","gamification","achievement"]},payload:Qs}),ws=C({name:"DailyGoalCompletedEventPayload",description:"Payload when a daily goal is completed",fields:{learnerId:{type:u.String_unsecure(),isOptional:!1},date:{type:u.String_unsecure(),isOptional:!1},targetXp:{type:u.Int_unsecure(),isOptional:!1},actualXp:{type:u.Int_unsecure(),isOptional:!1},completedAt:{type:u.DateTime(),isOptional:!1}}}),vs=K({meta:{key:"daily_goal.completed",version:"1.0.0",description:"A daily goal has been completed.",stability:"stable",owners:[...P],tags:["learning","gamification","goal"]},payload:ws}),Ys=C({name:"CertificateIssuedEventPayload",description:"Payload when a certificate is issued",fields:{certificateId:{type:u.String_unsecure(),isOptional:!1},learnerId:{type:u.String_unsecure(),isOptional:!1},courseId:{type:u.String_unsecure(),isOptional:!1},certificateNumber:{type:u.String_unsecure(),isOptional:!1},issuedAt:{type:u.DateTime(),isOptional:!1}}}),Gs=K({meta:{key:"certificate.issued",version:"1.0.0",description:"A certificate has been issued.",stability:"stable",owners:[...P],tags:["learning","certificate"]},payload:Ys}),We={CoursePublishedEvent:Is,EnrollmentCreatedEvent:Os,LessonCompletedEvent:Ds,CourseCompletedEvent:js,OnboardingStartedEvent:Ks,OnboardingStepCompletedEvent:ys,OnboardingCompletedEvent:qs,CardReviewedEvent:xs,QuizStartedEvent:Ls,QuizCompletedEvent:$s,XpEarnedEvent:Ws,LevelUpEvent:Xs,StreakUpdatedEvent:Fs,AchievementUnlockedEvent:Us,DailyGoalCompletedEvent:vs,CertificateIssuedEvent:Gs};import{defineFeature as Ss}from"@contractspec/lib.contracts-spec/features";var ze=Ss({meta:{key:"modules.learning-journey",version:"1.0.0",title:"Learning Journey",description:"Comprehensive learning journey engine - onboarding, LMS, flashcards, gamification, and AI personalization",domain:"learning-journey",owners:["@contractspec-core"],tags:["package","modules","learning-journey"],stability:"experimental"},operations:[{key:"learning.journey.listTracks",version:"1.0.0"},{key:"learning.journey.getProgress",version:"1.0.0"},{key:"learning.journey.recordEvent",version:"1.0.0"},{key:"learning.enroll",version:"1.0.0"},{key:"learning.completeLesson",version:"1.0.0"},{key:"learning.submitCardReview",version:"1.0.0"},{key:"learning.getDueCards",version:"1.0.0"},{key:"learning.getDashboard",version:"1.0.0"}],events:[{key:"course.published",version:"1.0.0"},{key:"enrollment.created",version:"1.0.0"},{key:"lesson.completed",version:"1.0.0"},{key:"course.completed",version:"1.0.0"},{key:"onboarding.started",version:"1.0.0"},{key:"onboarding.step_completed",version:"1.0.0"},{key:"onboarding.completed",version:"1.0.0"},{key:"flashcard.reviewed",version:"1.0.0"},{key:"quiz.started",version:"1.0.0"},{key:"quiz.completed",version:"1.0.0"},{key:"xp.earned",version:"1.0.0"},{key:"level.up",version:"1.0.0"},{key:"streak.updated",version:"1.0.0"},{key:"achievement.unlocked",version:"1.0.0"},{key:"daily_goal.completed",version:"1.0.0"},{key:"certificate.issued",version:"1.0.0"}]});var or=(r,t)=>{if(!r)return!0;if(!t)return!1;return Object.entries(r).every(([o,s])=>t[o]===s)},N=(r,t)=>{if(r.eventName!==t.name)return!1;if(r.eventVersion!==void 0&&t.version!==void 0&&r.eventVersion!==t.version)return!1;if(r.sourceModule&&t.sourceModule&&r.sourceModule!==t.sourceModule)return!1;return or(r.payloadFilter,t.payload)},T=(r,t,o,s)=>{if(r.kind==="count"){if(!N(r,t))return{matched:!1};let c=o.occurrences+1;return{matched:(r.withinHours===void 0||s!==void 0&&t.occurredAt!==void 0&&(t.occurredAt.getTime()-s.getTime())/3600000<=r.withinHours)&&c>=r.atLeast,occurrences:c}}if(r.kind==="time_window"){if(!N(r,t))return{matched:!1};if(r.availableAfterHours!==void 0&&s!==void 0&&t.occurredAt!==void 0&&(t.occurredAt.getTime()-s.getTime())/3600000<r.availableAfterHours)return{matched:!1};if(r.withinHoursOfStart!==void 0&&s!==void 0&&t.occurredAt!==void 0&&(t.occurredAt.getTime()-s.getTime())/3600000>r.withinHoursOfStart)return{matched:!1};return{matched:!0}}if(r.kind==="mastery"){if(t.name!==r.eventName)return{matched:!1};if(!or(r.payloadFilter,t.payload))return{matched:!1};let c=r.masteryField??"mastery",A=t.payload?.[c];if(typeof A!=="number")return{matched:!1};if(A<r.minimumMastery)return{matched:!1};let I=o.masteryCount+1;return{masteryCount:I,matched:I>=(r.requiredCount??1)}}return{matched:N(r,t)}},er=(r,t)=>{if(!r||!t)return{};let o=t.getTime(),s=o;if(r.unlockOnDay!==void 0)s=o+(r.unlockOnDay-1)*24*60*60*1000;if(r.unlockAfterHours!==void 0)s=o+r.unlockAfterHours*60*60*1000;let c=new Date(s),A=r.dueWithinHours!==void 0?new Date(c.getTime()+r.dueWithinHours*60*60*1000):void 0;return{availableAt:c,dueAt:A}},nr=(r,t)=>{let o=t.steps.find((s)=>s.stepId===r.stepId);if(!o)return!1;if(r.kind==="step_completed")return o.status==="COMPLETED"||o.status==="SKIPPED";return o.selectedBranchKey===r.branchKey},pr=(r,t,o,s)=>{if(!r.branches?.length)return;if(!t)return r.branches.find((c)=>c.when===void 0);for(let c of r.branches){if(!c.when)continue;if(T(c.when,t,o,s).matched)return c}return r.branches.find((c)=>c.when===void 0)};var Rs=(r)=>r.map((t)=>({...t})),Ns=(r,t)=>{let o=new Map;for(let s of r.steps){let c=t.steps.find((I)=>I.stepId===s.id),A=s.branches?.find((I)=>I.key===c?.selectedBranchKey);if(!A?.blockStepIds?.length)continue;for(let I of A.blockStepIds)o.set(I,{blockedByBranchKey:A.key,blockedByStepId:s.id})}return o},M=(r,t,o={})=>{let s=o.now??new Date,c={...t,badges:[...t.badges],eventLog:[...t.eventLog],steps:Rs(t.steps),streak:{...t.streak}},A=Ns(r,c);for(let I of r.steps){let O=c.steps.find((X)=>X.stepId===I.id);if(!O)continue;let{availableAt:j,dueAt:D}=er(I.availability,c.startedAt);if(O.availableAt=j,O.dueAt=D,O.status==="COMPLETED"||O.status==="SKIPPED")continue;let q=A.get(I.id);if(q){O.blockedAt??=s,O.blockedByBranchKey=q.blockedByBranchKey,O.blockedByStepId=q.blockedByStepId,O.status="BLOCKED";continue}let m=I.prerequisites??[],B=I.prerequisiteMode==="any"?m.some((X)=>nr(X,c)):m.every((X)=>nr(X,c)),ut=(m.length===0?!0:B)&&(!j||s.getTime()>=j.getTime());if(D&&s.getTime()>D.getTime()){O.missedAt??=s,O.status="MISSED";continue}O.status=ut?"AVAILABLE":"LOCKED"}return c},d=(r)=>{let t=r.steps.filter((o)=>o.status!=="BLOCKED");return t.length>0&&t.every((o)=>o.status==="COMPLETED"||o.status==="SKIPPED")},ur=(r,t,o={})=>{let s=M(r,t,o),c=s.steps.filter((B)=>B.status!=="BLOCKED"),A=s.steps.filter((B)=>B.status==="COMPLETED"),I=s.steps.filter((B)=>B.status==="AVAILABLE").map((B)=>B.stepId),O=s.steps.filter((B)=>B.status==="BLOCKED").map((B)=>B.stepId),j=s.steps.filter((B)=>B.status==="MISSED").map((B)=>B.stepId),D=c.filter((B)=>B.status==="COMPLETED"||B.status==="SKIPPED").length,q=c.length>0?Math.round(D/c.length*100):0,m=I[0]??null;return{activeStepCount:c.length,availableStepIds:I,badges:[...s.badges],blockedStepIds:O,completedAt:s.completedAt,completedStepCount:D,completedStepIds:A.map((B)=>B.stepId),currentStepId:m,isCompleted:d(s),lastActivityAt:s.lastActivityAt,learnerId:s.learnerId,missedStepIds:j,nextStepId:m,progressPercent:q,startedAt:s.startedAt,steps:s.steps,streakDays:s.streak.currentStreak,totalSteps:r.steps.length,trackId:s.trackId,xpEarned:s.xpEarned}};var cr=new U,tt=new v,st=(r)=>({...r,badges:[...r.badges],eventLog:[...r.eventLog],steps:r.steps.map((t)=>({...t})),streak:{...r.streak}}),Ts=(r)=>r.reward?.xp??r.xpReward??0,ot=(r,t)=>r.steps.find((o)=>o.stepId===t),et=(r,t)=>{if(t&&!r.badges.includes(t))r.badges.push(t)},nt=(r,t,o,s)=>{if(!d(t)||t.completionRewardApplied)return t;let c=r.completionRewards?.xp??0;if(c>0)t.xpEarned+=s.calculate({activity:"journey_complete",baseXp:c,currentStreak:t.streak.currentStreak}).totalXp;return et(t,r.completionRewards?.badgeKey),t.completedAt=o,t.completionRewardApplied=!0,t},pt=(r,t,o,s,c,A,I,O)=>{if(s.status==="COMPLETED")return;let j=Ts(o);if(j>0)t.xpEarned+=O.calculate({activity:"journey_step",baseXp:j,currentStreak:t.streak.currentStreak}).totalXp;s.completedAt=c,s.eventPayload=A?.payload,s.manual=I,s.status="COMPLETED",s.triggeringEvent=A?.name??"journey.step.manual",s.xpEarned=j;let D=pr(o,A,s,t.startedAt);if(!D)return;if(s.selectedBranchKey=D.key,D.reward?.xp)t.xpEarned+=D.reward.xp;et(t,D.reward?.badgeKey)},ds=(r,t={})=>M(r,{badges:[],completionRewardApplied:!1,eventLog:[],learnerId:t.learnerId,startedAt:t.now,steps:r.steps.map((o)=>({masteryCount:0,occurrences:0,status:"LOCKED",stepId:o.id,xpEarned:0})),streak:cr.getInitialState(),trackId:r.id,xpEarned:0},{now:t.now}),Es=(r,t,o,s={})=>{let c=s.streakEngine??cr,A=s.xpEngine??tt,I=o.occurredAt??new Date,O=M(r,st(t),{now:I});O.eventLog.push({...o,occurredAt:I,trackId:r.id}),O.lastActivityAt=I,O.startedAt??=I,O.streak=c.update(O.streak,I).state;for(let j of r.steps){let D=ot(O,j.id);if(!D||D.status!=="AVAILABLE")continue;let q=T(j.completion,o,D,O.startedAt);if(q.occurrences!==void 0)D.occurrences=q.occurrences;if(q.masteryCount!==void 0)D.masteryCount=q.masteryCount;if(!q.matched)continue;pt(r,O,j,D,I,o,!1,A)}return nt(r,M(r,O,{now:I}),I,A)},fs=(r,t,o,s={})=>{let c=s.streakEngine??cr,A=s.xpEngine??tt,I=s.now??new Date,O=M(r,st(t),{now:I}),j=r.steps.find((q)=>q.id===o),D=ot(O,o);if(!j||!D||D.status!=="AVAILABLE")return O;return O.eventLog.push({name:"journey.step.manual",occurredAt:I,trackId:r.id}),O.lastActivityAt=I,O.startedAt??=I,O.streak=c.update(O.streak,I).state,pt(r,O,j,D,I,{name:"journey.step.manual",occurredAt:I},!0,A),nt(r,M(r,O,{now:I}),I,A)},ls=(r,t,o={})=>ur(r,t,o);export{To as xpEngine,M as synchronizeJourneyProgressState,Zo as streakEngine,Vo as srsEngine,pr as resolveJourneyBranch,er as resolveJourneyAvailability,Es as recordJourneyEvent,rt as quizEnums,lr as quizEntities,ls as projectJourneyProgress,or as matchesPayloadFilter,T as matchesJourneyCondition,N as matchesBaseJourneyEvent,Ke as learningJourneySchemaContribution,bs as learningJourneyEnums,hs as learningJourneyEntities,Rr as learnerEnums,Sr as learnerEntities,Tr as journeyEnums,Nr as journeyEntities,d as isJourneyComplete,No as getXpSourceLabel,wr as gamificationEnums,Ur as gamificationEntities,Xr as flashcardEnums,mr as flashcardEntities,ds as createJourneyProgressState,Mr as courseEnums,$r as courseEntities,fs as completeJourneyStep,ur as buildJourneyProgressSnapshot,Hr as aiEnums,qr as aiEntities,Ws as XpEarnedEvent,Tt as XPTransactionEntity,v as XPEngine,l as SuccessOutput,br as SubmitCardReviewInput,Do as SubmitCardReviewContract,Fs as StreakUpdatedEvent,Gt as StreakEntity,U as StreakEngine,Vt as SkillMapEntity,Js as SkillAssessmentEntity,Dr as SRSEngine,uo as RecordJourneyEventContract,_r as RecommendationTypeEnum,Zt as RecommendationEntity,Er as QuizStatusEnum,Ls as QuizStartedEvent,us as QuizEntity,$s as QuizCompletedEvent,ks as QuizAttemptEntity,dr as QuestionTypeEnum,gs as QuestionOptionEntity,cs as QuestionEntity,Gr as ProgressStatusEnum,Jo as ProgressModel,ys as OnboardingStepCompletedEvent,Ks as OnboardingStartedEvent,qs as OnboardingCompletedEvent,lt as ModuleCompletionEntity,no as ListJourneyTracksContract,Xs as LevelUpEvent,Lr as LessonTypeEnum,ft as LessonProgressEntity,mt as LessonEntity,Xt as LessonContentEntity,Ds as LessonCompletedEvent,yr as LearningStyleEnum,Lt as LearningPathEntity,ze as LearningJourneyFeature,We as LearningJourneyEvents,$t as LearningGapEntity,xt as LearnerProfileEntity,Ot as LearnerModel,dt as LearnerEntity,Or as LearnerDashboardModel,Yt as LearnerAchievementEntity,Qr as LeaderboardPeriodEnum,Rt as LeaderboardEntryEntity,P as LEARNING_JOURNEY_OWNERS,Jt as JourneyTrackModel,os as JourneyTrackEntity,ss as JourneyStepStatusEnum,ps as JourneyStepProgressEntity,es as JourneyStepEntity,E as JourneyProgressModel,ns as JourneyProgressEntity,Nt as HeartEntity,Ar as GetLearnerDashboardInput,jo as GetLearnerDashboardContract,po as GetJourneyProgressContract,Ir as GetDueCardsOutput,ar as GetDueCardsInput,Bo as GetDueCardsContract,Yr as EnrollmentStatusEnum,f as EnrollmentModel,Et as EnrollmentEntity,Os as EnrollmentCreatedEvent,Jr as EnrollInCourseInput,Oo as EnrollInCourseContract,ho as DeckModel,Ft as DeckEntity,St as DailyGoalEntity,vs as DailyGoalCompletedEvent,w as DEFAULT_XP_CONFIG,Kt as DEFAULT_STREAK_CONFIG,it as DEFAULT_SRS_CONFIG,Vr as CourseStatusEnum,Is as CoursePublishedEvent,Wt as CourseModuleEntity,ko as CourseModel,Mt as CourseEntity,xr as CourseDifficultyEnum,js as CourseCompletedEvent,Zr as ContentTypeEnum,hr as CompleteLessonInput,Po as CompleteLessonContract,Gs as CertificateIssuedEvent,rs as CertificateEntity,wt as CardScheduleEntity,xs as CardReviewedEvent,Ut as CardReviewEntity,Wr as CardRatingEnum,Pt as CardModel,Qt as CardEntity,fr as AttemptStatusEnum,Us as AchievementUnlockedEvent,Fr as AchievementTypeEnum,Dt as AchievementModel,vt as AchievementEntity};
@@ -1 +1 @@
1
- import{defineFeature as g}from"@contractspec/lib.contracts-spec";var j=g({meta:{key:"learning-journey",version:"1.0.0",title:"Learning Journey",description:"Learning platform with courses, onboarding, flashcards, and gamification",domain:"learning",owners:["@platform.learning-journey"],tags:["learning","onboarding","courses","flashcards","gamification"],stability:"stable"},operations:[{key:"learning.journey.recordEvent",version:"1.0.0"},{key:"learning.journey.listTracks",version:"1.0.0"},{key:"learning.journey.getProgress",version:"1.0.0"},{key:"learning.enroll",version:"1.0.0"},{key:"learning.completeLesson",version:"1.0.0"},{key:"learning.submitCardReview",version:"1.0.0"},{key:"learning.getDueCards",version:"1.0.0"},{key:"learning.getDashboard",version:"1.0.0"}],events:[{key:"course.published",version:"1.0.0"},{key:"course.completed",version:"1.0.0"},{key:"enrollment.created",version:"1.0.0"},{key:"lesson.completed",version:"1.0.0"},{key:"onboarding.started",version:"1.0.0"},{key:"onboarding.step_completed",version:"1.0.0"},{key:"onboarding.completed",version:"1.0.0"},{key:"flashcard.reviewed",version:"1.0.0"},{key:"quiz.started",version:"1.0.0"},{key:"quiz.completed",version:"1.0.0"},{key:"xp.earned",version:"1.0.0"},{key:"level.up",version:"1.0.0"},{key:"streak.updated",version:"1.0.0"},{key:"achievement.unlocked",version:"1.0.0"},{key:"daily_goal.completed",version:"1.0.0"},{key:"certificate.issued",version:"1.0.0"}],presentations:[],opToPresentation:[],presentationsTargets:[],capabilities:{provides:[{key:"learning-journey",version:"1.0.0"},{key:"onboarding",version:"1.0.0"},{key:"gamification",version:"1.0.0"}],requires:[{key:"identity",version:"1.0.0"}]}});export{j as LearningJourneyFeature};
1
+ import{defineFeature as g}from"@contractspec/lib.contracts-spec/features";var j=g({meta:{key:"modules.learning-journey",version:"1.0.0",title:"Learning Journey",description:"Comprehensive learning journey engine - onboarding, LMS, flashcards, gamification, and AI personalization",domain:"learning-journey",owners:["@contractspec-core"],tags:["package","modules","learning-journey"],stability:"experimental"},operations:[{key:"learning.journey.listTracks",version:"1.0.0"},{key:"learning.journey.getProgress",version:"1.0.0"},{key:"learning.journey.recordEvent",version:"1.0.0"},{key:"learning.enroll",version:"1.0.0"},{key:"learning.completeLesson",version:"1.0.0"},{key:"learning.submitCardReview",version:"1.0.0"},{key:"learning.getDueCards",version:"1.0.0"},{key:"learning.getDashboard",version:"1.0.0"}],events:[{key:"course.published",version:"1.0.0"},{key:"enrollment.created",version:"1.0.0"},{key:"lesson.completed",version:"1.0.0"},{key:"course.completed",version:"1.0.0"},{key:"onboarding.started",version:"1.0.0"},{key:"onboarding.step_completed",version:"1.0.0"},{key:"onboarding.completed",version:"1.0.0"},{key:"flashcard.reviewed",version:"1.0.0"},{key:"quiz.started",version:"1.0.0"},{key:"quiz.completed",version:"1.0.0"},{key:"xp.earned",version:"1.0.0"},{key:"level.up",version:"1.0.0"},{key:"streak.updated",version:"1.0.0"},{key:"achievement.unlocked",version:"1.0.0"},{key:"daily_goal.completed",version:"1.0.0"},{key:"certificate.issued",version:"1.0.0"}]});export{j as LearningJourneyFeature};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/module.learning-journey",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "Comprehensive learning journey engine - onboarding, LMS, flashcards, gamification, and AI personalization",
5
5
  "keywords": [
6
6
  "contractspec",
@@ -29,13 +29,13 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@contractspec/lib.schema": "3.7.14",
32
- "@contractspec/lib.contracts-spec": "5.4.0",
32
+ "@contractspec/lib.contracts-spec": "5.5.1",
33
33
  "zod": "^4.3.5"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@contractspec/tool.typescript": "3.7.13",
37
37
  "typescript": "^5.9.3",
38
- "@contractspec/tool.bun": "3.7.14"
38
+ "@contractspec/tool.bun": "3.7.17"
39
39
  },
40
40
  "exports": {
41
41
  ".": {