@contractspec/module.learning-journey 3.7.18 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/browser/contracts/index.js +1 -1
- package/dist/browser/contracts/journey.js +1 -0
- package/dist/browser/contracts/operations.js +1 -1
- package/dist/browser/docs/index.js +13 -12
- package/dist/browser/docs/learning-journey.docblock.js +13 -12
- package/dist/browser/engines/index.js +1 -1
- package/dist/browser/engines/xp.js +1 -1
- package/dist/browser/entities/index.js +1 -1
- package/dist/browser/entities/journey.js +1 -0
- package/dist/browser/index.js +14 -13
- package/dist/browser/learning-journey.feature.js +1 -1
- package/dist/browser/runtime/index.js +1 -0
- package/dist/browser/runtime/matchers.js +1 -0
- package/dist/browser/runtime/progress-state.js +1 -0
- package/dist/browser/runtime/snapshot.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/{onboarding.d.ts → journey.d.ts} +477 -180
- package/dist/contracts/journey.js +2 -0
- package/dist/contracts/operations.js +1 -1
- package/dist/docs/index.js +13 -12
- package/dist/docs/learning-journey.docblock.js +13 -12
- package/dist/engines/index.js +1 -1
- package/dist/engines/xp.d.ts +1 -1
- package/dist/engines/xp.js +1 -1
- package/dist/entities/index.d.ts +28 -27
- package/dist/entities/index.js +1 -1
- package/dist/entities/{onboarding.d.ts → journey.d.ts} +61 -74
- package/dist/entities/journey.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +14 -13
- package/dist/learning-journey.feature.d.ts +0 -4
- package/dist/learning-journey.feature.js +1 -1
- package/dist/node/contracts/index.js +1 -1
- package/dist/node/contracts/journey.js +1 -0
- package/dist/node/contracts/operations.js +1 -1
- package/dist/node/docs/index.js +13 -12
- package/dist/node/docs/learning-journey.docblock.js +13 -12
- package/dist/node/engines/index.js +1 -1
- package/dist/node/engines/xp.js +1 -1
- package/dist/node/entities/index.js +1 -1
- package/dist/node/entities/journey.js +1 -0
- package/dist/node/index.js +14 -13
- package/dist/node/learning-journey.feature.js +1 -1
- package/dist/node/runtime/index.js +1 -0
- package/dist/node/runtime/matchers.js +1 -0
- package/dist/node/runtime/progress-state.js +1 -0
- package/dist/node/runtime/snapshot.js +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/journey-runtime.test.d.ts +1 -0
- package/dist/runtime/matchers.d.ts +20 -0
- package/dist/runtime/matchers.js +2 -0
- package/dist/runtime/progress-state.d.ts +19 -0
- package/dist/runtime/progress-state.js +2 -0
- package/dist/runtime/snapshot.d.ts +8 -0
- package/dist/runtime/snapshot.js +2 -0
- package/dist/track-spec.d.ts +118 -87
- package/package.json +87 -31
- package/dist/browser/contracts/onboarding.js +0 -1
- package/dist/browser/entities/onboarding.js +0 -1
- package/dist/contracts/onboarding.js +0 -2
- package/dist/entities/onboarding.js +0 -2
- package/dist/node/contracts/onboarding.js +0 -1
- package/dist/node/entities/onboarding.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineSchemaModel as s,ScalarTypeEnum as e}from"@contractspec/lib.schema";var z=s({name:"Course",description:"A learning course",fields:{id:{type:e.String_unsecure(),isOptional:!1},title:{type:e.String_unsecure(),isOptional:!1},slug:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},difficulty:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},estimatedDuration:{type:e.Int_unsecure(),isOptional:!0},thumbnailUrl:{type:e.String_unsecure(),isOptional:!0},createdAt:{type:e.DateTime(),isOptional:!1}}}),x=s({name:"Learner",description:"A learner profile",fields:{id:{type:e.String_unsecure(),isOptional:!1},userId:{type:e.String_unsecure(),isOptional:!1},displayName:{type:e.String_unsecure(),isOptional:!0},level:{type:e.Int_unsecure(),isOptional:!1},totalXp:{type:e.Int_unsecure(),isOptional:!1},currentStreak:{type:e.Int_unsecure(),isOptional:!1},longestStreak:{type:e.Int_unsecure(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),o=s({name:"Enrollment",description:"A course enrollment",fields:{id:{type:e.String_unsecure(),isOptional:!1},learnerId:{type:e.String_unsecure(),isOptional:!1},courseId:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},progress:{type:e.Int_unsecure(),isOptional:!1},startedAt:{type:e.DateTime(),isOptional:!0},completedAt:{type:e.DateTime(),isOptional:!0}}}),F=s({name:"LessonProgress",description:"Lesson progress",fields:{id:{type:e.String_unsecure(),isOptional:!1},learnerId:{type:e.String_unsecure(),isOptional:!1},lessonId:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},progress:{type:e.Int_unsecure(),isOptional:!1},score:{type:e.Int_unsecure(),isOptional:!0},timeSpent:{type:e.Int_unsecure(),isOptional:!1},completedAt:{type:e.DateTime(),isOptional:!0}}}),H=s({name:"Deck",description:"A flashcard deck",fields:{id:{type:e.String_unsecure(),isOptional:!1},title:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},cardCount:{type:e.Int_unsecure(),isOptional:!1},isPublic:{type:e.Boolean(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),y=s({name:"Card",description:"A flashcard",fields:{id:{type:e.String_unsecure(),isOptional:!1},deckId:{type:e.String_unsecure(),isOptional:!1},front:{type:e.String_unsecure(),isOptional:!1},back:{type:e.String_unsecure(),isOptional:!1},hints:{type:e.JSON(),isOptional:!0},isDue:{type:e.Boolean(),isOptional:!1},nextReviewAt:{type:e.DateTime(),isOptional:!0}}}),D=s({name:"Achievement",description:"An achievement",fields:{id:{type:e.String_unsecure(),isOptional:!1},key:{type:e.String_unsecure(),isOptional:!1},name:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!1},icon:{type:e.String_unsecure(),isOptional:!0},xpReward:{type:e.Int_unsecure(),isOptional:!1},unlockedAt:{type:e.DateTime(),isOptional:!0}}}),l=s({name:"EnrollInCourseInput",description:"Input for enrolling in a course",fields:{courseId:{type:e.String_unsecure(),isOptional:!1}}}),u=s({name:"CompleteLessonInput",description:"Input for completing a lesson",fields:{lessonId:{type:e.String_unsecure(),isOptional:!1},score:{type:e.Int_unsecure(),isOptional:!0},timeSpent:{type:e.Int_unsecure(),isOptional:!1}}}),a=s({name:"SubmitCardReviewInput",description:"Input for submitting a card review",fields:{cardId:{type:e.String_unsecure(),isOptional:!1},rating:{type:e.String_unsecure(),isOptional:!1},responseTimeMs:{type:e.Int_unsecure(),isOptional:!0}}}),f=s({name:"GetDueCardsInput",description:"Input for getting due cards",fields:{deckId:{type:e.String_unsecure(),isOptional:!0},limit:{type:e.Int_unsecure(),isOptional:!0}}}),O=s({name:"GetDueCardsOutput",description:"Output for getting due cards",fields:{cards:{type:y,isArray:!0,isOptional:!1},total:{type:e.Int_unsecure(),isOptional:!1}}}),d=s({name:"GetLearnerDashboardInput",description:"Input for getting learner dashboard",fields:{learnerId:{type:e.String_unsecure(),isOptional:!0}}}),c=s({name:"LearnerDashboard",description:"Learner dashboard data",fields:{learner:{type:x,isOptional:!1},currentStreak:{type:e.Int_unsecure(),isOptional:!1},dailyXpGoal:{type:e.Int_unsecure(),isOptional:!1},dailyXpProgress:{type:e.Int_unsecure(),isOptional:!1},activeEnrollments:{type:o,isArray:!0,isOptional:!1},recentAchievements:{type:D,isArray:!0,isOptional:!1},dueCardCount:{type:e.Int_unsecure(),isOptional:!1}}}),r=s({name:"SuccessOutput",description:"Generic success output",fields:{success:{type:e.Boolean(),isOptional:!1},xpEarned:{type:e.Int_unsecure(),isOptional:!0}}});var i=["modules.learning-journey"];import{defineCommand as A,defineQuery as _}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as n,ScalarTypeEnum as t}from"@contractspec/lib.schema";var k=n({name:"OnboardingStepCondition",description:"Structured completion condition for onboarding steps.",fields:{eventName:{type:t.String_unsecure(),isOptional:!1},eventVersion:{type:t.String_unsecure(),isOptional:!0},sourceModule:{type:t.String_unsecure(),isOptional:!0},payloadFilter:{type:t.JSON(),isOptional:!0}}}),v=n({name:"OnboardingStep",description:"Declarative onboarding step definition.",fields:{id:{type:t.String_unsecure(),isOptional:!1},trackId:{type:t.String_unsecure(),isOptional:!1},title:{type:t.String_unsecure(),isOptional:!1},description:{type:t.String_unsecure(),isOptional:!0},instructions:{type:t.String_unsecure(),isOptional:!0},helpUrl:{type:t.String_unsecure(),isOptional:!0},order:{type:t.Int_unsecure(),isOptional:!1},completionEvent:{type:t.String_unsecure(),isOptional:!1},completionCondition:{type:k,isOptional:!0},xpReward:{type:t.Int_unsecure(),isOptional:!0},isRequired:{type:t.Boolean(),isOptional:!0},canSkip:{type:t.Boolean(),isOptional:!0},actionUrl:{type:t.String_unsecure(),isOptional:!0},actionLabel:{type:t.String_unsecure(),isOptional:!0},metadata:{type:t.JSON(),isOptional:!0}}}),C=n({name:"OnboardingTrack",description:"Onboarding track metadata and steps.",fields:{id:{type:t.String_unsecure(),isOptional:!1},productId:{type:t.String_unsecure(),isOptional:!0},name:{type:t.String_unsecure(),isOptional:!1},description:{type:t.String_unsecure(),isOptional:!0},targetUserSegment:{type:t.String_unsecure(),isOptional:!0},targetRole:{type:t.String_unsecure(),isOptional:!0},isActive:{type:t.Boolean(),isOptional:!0},isRequired:{type:t.Boolean(),isOptional:!0},canSkip:{type:t.Boolean(),isOptional:!0},totalXp:{type:t.Int_unsecure(),isOptional:!0},completionXpBonus:{type:t.Int_unsecure(),isOptional:!0},completionBadgeKey:{type:t.String_unsecure(),isOptional:!0},streakHoursWindow:{type:t.Int_unsecure(),isOptional:!0},streakBonusXp:{type:t.Int_unsecure(),isOptional:!0},metadata:{type:t.JSON(),isOptional:!0},steps:{type:v,isArray:!0,isOptional:!1}}}),w=n({name:"OnboardingStepProgress",description:"Progress for a specific onboarding step.",fields:{stepId:{type:t.String_unsecure(),isOptional:!1},status:{type:t.String_unsecure(),isOptional:!1},xpEarned:{type:t.Int_unsecure(),isOptional:!0},triggeringEvent:{type:t.String_unsecure(),isOptional:!0},eventPayload:{type:t.JSON(),isOptional:!0},completedAt:{type:t.DateTime(),isOptional:!0}}}),g=n({name:"OnboardingProgress",description:"Aggregated progress for an onboarding track.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!0},trackId:{type:t.String_unsecure(),isOptional:!1},progress:{type:t.Int_unsecure(),isOptional:!1},isCompleted:{type:t.Boolean(),isOptional:!1},xpEarned:{type:t.Int_unsecure(),isOptional:!0},startedAt:{type:t.DateTime(),isOptional:!0},completedAt:{type:t.DateTime(),isOptional:!0},lastActivityAt:{type:t.DateTime(),isOptional:!0},steps:{type:w,isArray:!0,isOptional:!0}}}),B=n({name:"ListOnboardingTracksInput",description:"Filters for listing onboarding tracks.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!0},productId:{type:t.String_unsecure(),isOptional:!0},trackIds:{type:t.String_unsecure(),isArray:!0,isOptional:!0},includeProgress:{type:t.Boolean(),isOptional:!0}}}),X=n({name:"ListOnboardingTracksOutput",description:"Available onboarding tracks with optional progress.",fields:{tracks:{type:C,isArray:!0,isOptional:!1},progress:{type:g,isArray:!0,isOptional:!0}}}),h=n({name:"GetOnboardingProgressInput",description:"Input for fetching onboarding progress for a track.",fields:{trackId:{type:t.String_unsecure(),isOptional:!1},learnerId:{type:t.String_unsecure(),isOptional:!0}}}),j=n({name:"RecordOnboardingEventInput",description:"Record a domain event to advance onboarding progress via completion conditions.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!1},trackId:{type:t.String_unsecure(),isOptional:!0},eventName:{type:t.String_unsecure(),isOptional:!1},eventVersion:{type:t.String_unsecure(),isOptional:!0},eventPayload:{type:t.JSON(),isOptional:!0},occurredAt:{type:t.DateTime(),isOptional:!0}}}),G=_({meta:{key:"learning.onboarding.listTracks",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","onboarding","journey"],description:"List onboarding tracks available to a learner or product.",goal:"Expose track catalog for UI/API surfaces.",context:"Called when showing onboarding/learning journey catalog."},io:{input:B,output:X},policy:{auth:"user"}}),b=_({meta:{key:"learning.onboarding.getProgress",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","onboarding","journey"],description:"Fetch onboarding progress for a specific track.",goal:"Display learner progress and remaining steps.",context:"Called when rendering a track detail or widget."},io:{input:h,output:g},policy:{auth:"user"}}),Q=A({meta:{key:"learning.onboarding.recordEvent",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","onboarding","events"],description:"Record a domain event to evaluate onboarding step completion conditions.",goal:"Advance onboarding automatically from product events.",context:"Called by event bus handlers when relevant product events fire (e.g., deal.created)."},io:{input:j,output:r,errors:{TRACK_NOT_FOUND:{description:"Track not found for event routing",http:404,gqlCode:"TRACK_NOT_FOUND",when:"Track ID or routing context is invalid"},STEP_NOT_FOUND:{description:"Step not found for completion condition",http:404,gqlCode:"STEP_NOT_FOUND",when:"No step matches the incoming event"}}},policy:{auth:"user"}});import{defineCommand as p,defineQuery as I}from"@contractspec/lib.contracts-spec";var Y=p({meta:{key:"learning.enroll",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","enrollment"],description:"Enroll in a course.",goal:"Start learning a new course.",context:"Called when a learner wants to start a course."},io:{input:l,output:o,errors:{COURSE_NOT_FOUND:{description:"Course does not exist",http:404,gqlCode:"COURSE_NOT_FOUND",when:"Course ID is invalid"},ALREADY_ENROLLED:{description:"Already enrolled in course",http:409,gqlCode:"ALREADY_ENROLLED",when:"Learner is already enrolled"}}},policy:{auth:"user"}}),R=p({meta:{key:"learning.completeLesson",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","progress"],description:"Mark a lesson as completed.",goal:"Record lesson completion and earn XP.",context:"Called when a learner finishes a lesson."},io:{input:u,output:r,errors:{LESSON_NOT_FOUND:{description:"Lesson does not exist",http:404,gqlCode:"LESSON_NOT_FOUND",when:"Lesson ID is invalid"},NOT_ENROLLED:{description:"Not enrolled in course",http:403,gqlCode:"NOT_ENROLLED",when:"Learner is not enrolled in the course"}}},policy:{auth:"user"}}),m=p({meta:{key:"learning.submitCardReview",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","flashcards"],description:"Submit a flashcard review.",goal:"Record review and update SRS schedule.",context:"Called when reviewing flashcards."},io:{input:a,output:r,errors:{CARD_NOT_FOUND:{description:"Card does not exist",http:404,gqlCode:"CARD_NOT_FOUND",when:"Card ID is invalid"},INVALID_RATING:{description:"Invalid rating",http:400,gqlCode:"INVALID_RATING",when:"Rating must be AGAIN, HARD, GOOD, or EASY"}}},policy:{auth:"user"}}),N=I({meta:{key:"learning.getDueCards",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","flashcards"],description:"Get flashcards due for review.",goal:"Get the next batch of cards to review.",context:"Called when starting a review session."},io:{input:f,output:O},policy:{auth:"user"}}),S=I({meta:{key:"learning.getDashboard",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","dashboard"],description:"Get learner dashboard data.",goal:"Display learner progress and stats.",context:"Called when viewing the learning dashboard."},io:{input:d,output:c},policy:{auth:"user"}});export{r as SuccessOutput,a as SubmitCardReviewInput,m as SubmitCardReviewContract,Q as RecordOnboardingEventContract,F as ProgressModel,C as OnboardingTrackModel,w as OnboardingStepProgressModel,v as OnboardingStepModel,g as OnboardingProgressModel,G as ListOnboardingTracksContract,x as LearnerModel,c as LearnerDashboardModel,i as LEARNING_JOURNEY_OWNERS,b as GetOnboardingProgressContract,d as GetLearnerDashboardInput,S as GetLearnerDashboardContract,O as GetDueCardsOutput,f as GetDueCardsInput,N as GetDueCardsContract,o as EnrollmentModel,l as EnrollInCourseInput,Y as EnrollInCourseContract,H as DeckModel,z as CourseModel,u as CompleteLessonInput,R as CompleteLessonContract,y as CardModel,D as AchievementModel};
|
|
1
|
+
var i=["modules.learning-journey"];import{defineCommand as D,defineQuery as u}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as n,ScalarTypeEnum as t}from"@contractspec/lib.schema";var A=n({name:"JourneyCondition",description:"Adaptive completion condition for a journey step.",fields:{kind:{type:t.String_unsecure(),isOptional:!0},eventName:{type:t.String_unsecure(),isOptional:!1},eventVersion:{type:t.Int_unsecure(),isOptional:!0},sourceModule:{type:t.String_unsecure(),isOptional:!0},payloadFilter:{type:t.JSON(),isOptional:!0},atLeast:{type:t.Int_unsecure(),isOptional:!0},withinHours:{type:t.Int_unsecure(),isOptional:!0},withinHoursOfStart:{type:t.Int_unsecure(),isOptional:!0},availableAfterHours:{type:t.Int_unsecure(),isOptional:!0},skillIdField:{type:t.String_unsecure(),isOptional:!0},masteryField:{type:t.String_unsecure(),isOptional:!0},minimumMastery:{type:t.Float_unsecure(),isOptional:!0},requiredCount:{type:t.Int_unsecure(),isOptional:!0}}}),f=n({name:"JourneyReward",description:"XP and badge rewards for journey outcomes.",fields:{badgeKey:{type:t.String_unsecure(),isOptional:!0},xp:{type:t.Int_unsecure(),isOptional:!0}}}),k=n({name:"JourneyStep",description:"Adaptive learning journey step definition.",fields:{id:{type:t.String_unsecure(),isOptional:!1},title:{type:t.String_unsecure(),isOptional:!1},description:{type:t.String_unsecure(),isOptional:!0},instructions:{type:t.String_unsecure(),isOptional:!0},helpUrl:{type:t.String_unsecure(),isOptional:!0},order:{type:t.Int_unsecure(),isOptional:!0},completion:{type:A,isOptional:!1},availability:{type:t.JSON(),isOptional:!0},prerequisites:{type:t.JSON(),isOptional:!0},prerequisiteMode:{type:t.String_unsecure(),isOptional:!0},branches:{type:t.JSON(),isOptional:!0},reward:{type:f,isOptional:!0},xpReward:{type:t.Int_unsecure(),isOptional:!0},isRequired:{type:t.Boolean(),isOptional:!0},canSkip:{type:t.Boolean(),isOptional:!0},actionUrl:{type:t.String_unsecure(),isOptional:!0},actionLabel:{type:t.String_unsecure(),isOptional:!0},metadata:{type:t.JSON(),isOptional:!0}}}),v=n({name:"JourneyTrack",description:"Headless adaptive journey track definition.",fields:{id:{type:t.String_unsecure(),isOptional:!1},productId:{type:t.String_unsecure(),isOptional:!0},name:{type:t.String_unsecure(),isOptional:!1},description:{type:t.String_unsecure(),isOptional:!0},targetUserSegment:{type:t.String_unsecure(),isOptional:!0},targetRole:{type:t.String_unsecure(),isOptional:!0},totalXp:{type:t.Int_unsecure(),isOptional:!0},isActive:{type:t.Boolean(),isOptional:!0},isRequired:{type:t.Boolean(),isOptional:!0},canSkip:{type:t.Boolean(),isOptional:!0},streakRule:{type:t.JSON(),isOptional:!0},completionRewards:{type:f,isOptional:!0},metadata:{type:t.JSON(),isOptional:!0},steps:{type:k,isArray:!0,isOptional:!1}}}),C=n({name:"JourneyStepProgress",description:"Persisted and projected state for a journey step.",fields:{stepId:{type:t.String_unsecure(),isOptional:!1},status:{type:t.String_unsecure(),isOptional:!1},xpEarned:{type:t.Int_unsecure(),isOptional:!1},selectedBranchKey:{type:t.String_unsecure(),isOptional:!0},availableAt:{type:t.DateTime(),isOptional:!0},dueAt:{type:t.DateTime(),isOptional:!0},completedAt:{type:t.DateTime(),isOptional:!0},skippedAt:{type:t.DateTime(),isOptional:!0},blockedAt:{type:t.DateTime(),isOptional:!0},missedAt:{type:t.DateTime(),isOptional:!0},triggeringEvent:{type:t.String_unsecure(),isOptional:!0},eventPayload:{type:t.JSON(),isOptional:!0},occurrences:{type:t.Int_unsecure(),isOptional:!0},masteryCount:{type:t.Int_unsecure(),isOptional:!0}}}),o=n({name:"JourneyProgress",description:"Projected adaptive journey progress snapshot.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!0},trackId:{type:t.String_unsecure(),isOptional:!1},progressPercent:{type:t.Int_unsecure(),isOptional:!1},isCompleted:{type:t.Boolean(),isOptional:!1},xpEarned:{type:t.Int_unsecure(),isOptional:!1},streakDays:{type:t.Int_unsecure(),isOptional:!1},badges:{type:t.String_unsecure(),isArray:!0,isOptional:!1},activeStepCount:{type:t.Int_unsecure(),isOptional:!1},completedStepCount:{type:t.Int_unsecure(),isOptional:!1},nextStepId:{type:t.String_unsecure(),isOptional:!0},currentStepId:{type:t.String_unsecure(),isOptional:!0},startedAt:{type:t.DateTime(),isOptional:!0},completedAt:{type:t.DateTime(),isOptional:!0},lastActivityAt:{type:t.DateTime(),isOptional:!0},steps:{type:C,isArray:!0,isOptional:!1}}}),b=n({name:"JourneyTrackListInput",description:"Filters for listing adaptive journey tracks.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!0},productId:{type:t.String_unsecure(),isOptional:!0},trackIds:{type:t.String_unsecure(),isArray:!0,isOptional:!0},includeProgress:{type:t.Boolean(),isOptional:!0}}}),B=n({name:"JourneyTrackListOutput",description:"Adaptive journey catalog with optional progress snapshots.",fields:{tracks:{type:v,isArray:!0,isOptional:!1},progress:{type:o,isArray:!0,isOptional:!0}}}),X=n({name:"JourneyProgressInput",description:"Input for fetching projected progress for one track.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!0},trackId:{type:t.String_unsecure(),isOptional:!1}}}),w=n({name:"JourneyRecordEventInput",description:"Domain event used to advance an adaptive journey.",fields:{learnerId:{type:t.String_unsecure(),isOptional:!1},trackId:{type:t.String_unsecure(),isOptional:!0},eventName:{type:t.String_unsecure(),isOptional:!1},eventVersion:{type:t.Int_unsecure(),isOptional:!0},sourceModule:{type:t.String_unsecure(),isOptional:!0},eventPayload:{type:t.JSON(),isOptional:!0},occurredAt:{type:t.DateTime(),isOptional:!0}}}),V=u({meta:{key:"learning.journey.listTracks",version:"1.0.0",stability:"stable",owners:[...i],tags:["adaptive-learning","journey","learning"],description:"List adaptive learning journeys for a learner or product.",goal:"Expose the adaptive journey catalog to UI and API surfaces.",context:"Called when rendering journey lists, launchers, or registries."},io:{input:b,output:B},policy:{auth:"user"}}),Z=u({meta:{key:"learning.journey.getProgress",version:"1.0.0",stability:"stable",owners:[...i],tags:["adaptive-learning","journey","progress"],description:"Project the latest adaptive journey progress for one track.",goal:"Render next-step, branch, and reward state consistently.",context:"Called by widgets, journey detail pages, and adaptive shells."},io:{input:X,output:o},policy:{auth:"user"}}),$=D({meta:{key:"learning.journey.recordEvent",version:"1.0.0",stability:"stable",owners:[...i],tags:["adaptive-learning","journey","events"],description:"Record a domain event and re-evaluate an adaptive journey.",goal:"Advance branch-aware journey progress from product activity.",context:"Called by event handlers when product activity may unlock or complete steps."},io:{input:w,output:o},policy:{auth:"user"}});import{defineSchemaModel as s,ScalarTypeEnum as e}from"@contractspec/lib.schema";var G=s({name:"Course",description:"A learning course",fields:{id:{type:e.String_unsecure(),isOptional:!1},title:{type:e.String_unsecure(),isOptional:!1},slug:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},difficulty:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},estimatedDuration:{type:e.Int_unsecure(),isOptional:!0},thumbnailUrl:{type:e.String_unsecure(),isOptional:!0},createdAt:{type:e.DateTime(),isOptional:!1}}}),j=s({name:"Learner",description:"A learner profile",fields:{id:{type:e.String_unsecure(),isOptional:!1},userId:{type:e.String_unsecure(),isOptional:!1},displayName:{type:e.String_unsecure(),isOptional:!0},level:{type:e.Int_unsecure(),isOptional:!1},totalXp:{type:e.Int_unsecure(),isOptional:!1},currentStreak:{type:e.Int_unsecure(),isOptional:!1},longestStreak:{type:e.Int_unsecure(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),r=s({name:"Enrollment",description:"A course enrollment",fields:{id:{type:e.String_unsecure(),isOptional:!1},learnerId:{type:e.String_unsecure(),isOptional:!1},courseId:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},progress:{type:e.Int_unsecure(),isOptional:!1},startedAt:{type:e.DateTime(),isOptional:!0},completedAt:{type:e.DateTime(),isOptional:!0}}}),Q=s({name:"LessonProgress",description:"Lesson progress",fields:{id:{type:e.String_unsecure(),isOptional:!1},learnerId:{type:e.String_unsecure(),isOptional:!1},lessonId:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},progress:{type:e.Int_unsecure(),isOptional:!1},score:{type:e.Int_unsecure(),isOptional:!0},timeSpent:{type:e.Int_unsecure(),isOptional:!1},completedAt:{type:e.DateTime(),isOptional:!0}}}),U=s({name:"Deck",description:"A flashcard deck",fields:{id:{type:e.String_unsecure(),isOptional:!1},title:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},cardCount:{type:e.Int_unsecure(),isOptional:!1},isPublic:{type:e.Boolean(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),h=s({name:"Card",description:"A flashcard",fields:{id:{type:e.String_unsecure(),isOptional:!1},deckId:{type:e.String_unsecure(),isOptional:!1},front:{type:e.String_unsecure(),isOptional:!1},back:{type:e.String_unsecure(),isOptional:!1},hints:{type:e.JSON(),isOptional:!0},isDue:{type:e.Boolean(),isOptional:!1},nextReviewAt:{type:e.DateTime(),isOptional:!0}}}),q=s({name:"Achievement",description:"An achievement",fields:{id:{type:e.String_unsecure(),isOptional:!1},key:{type:e.String_unsecure(),isOptional:!1},name:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!1},icon:{type:e.String_unsecure(),isOptional:!0},xpReward:{type:e.Int_unsecure(),isOptional:!1},unlockedAt:{type:e.DateTime(),isOptional:!0}}}),O=s({name:"EnrollInCourseInput",description:"Input for enrolling in a course",fields:{courseId:{type:e.String_unsecure(),isOptional:!1}}}),d=s({name:"CompleteLessonInput",description:"Input for completing a lesson",fields:{lessonId:{type:e.String_unsecure(),isOptional:!1},score:{type:e.Int_unsecure(),isOptional:!0},timeSpent:{type:e.Int_unsecure(),isOptional:!1}}}),g=s({name:"SubmitCardReviewInput",description:"Input for submitting a card review",fields:{cardId:{type:e.String_unsecure(),isOptional:!1},rating:{type:e.String_unsecure(),isOptional:!1},responseTimeMs:{type:e.Int_unsecure(),isOptional:!0}}}),_=s({name:"GetDueCardsInput",description:"Input for getting due cards",fields:{deckId:{type:e.String_unsecure(),isOptional:!0},limit:{type:e.Int_unsecure(),isOptional:!0}}}),a=s({name:"GetDueCardsOutput",description:"Output for getting due cards",fields:{cards:{type:h,isArray:!0,isOptional:!1},total:{type:e.Int_unsecure(),isOptional:!1}}}),I=s({name:"GetLearnerDashboardInput",description:"Input for getting learner dashboard",fields:{learnerId:{type:e.String_unsecure(),isOptional:!0}}}),c=s({name:"LearnerDashboard",description:"Learner dashboard data",fields:{learner:{type:j,isOptional:!1},currentStreak:{type:e.Int_unsecure(),isOptional:!1},dailyXpGoal:{type:e.Int_unsecure(),isOptional:!1},dailyXpProgress:{type:e.Int_unsecure(),isOptional:!1},activeEnrollments:{type:r,isArray:!0,isOptional:!1},recentAchievements:{type:q,isArray:!0,isOptional:!1},dueCardCount:{type:e.Int_unsecure(),isOptional:!1}}}),p=s({name:"SuccessOutput",description:"Generic success output",fields:{success:{type:e.Boolean(),isOptional:!1},xpEarned:{type:e.Int_unsecure(),isOptional:!0}}});import{defineCommand as l,defineQuery as x}from"@contractspec/lib.contracts-spec";var J=l({meta:{key:"learning.enroll",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","enrollment"],description:"Enroll in a course.",goal:"Start learning a new course.",context:"Called when a learner wants to start a course."},io:{input:O,output:r,errors:{COURSE_NOT_FOUND:{description:"Course does not exist",http:404,gqlCode:"COURSE_NOT_FOUND",when:"Course ID is invalid"},ALREADY_ENROLLED:{description:"Already enrolled in course",http:409,gqlCode:"ALREADY_ENROLLED",when:"Learner is already enrolled"}}},policy:{auth:"user"}}),R=l({meta:{key:"learning.completeLesson",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","progress"],description:"Mark a lesson as completed.",goal:"Record lesson completion and earn XP.",context:"Called when a learner finishes a lesson."},io:{input:d,output:p,errors:{LESSON_NOT_FOUND:{description:"Lesson does not exist",http:404,gqlCode:"LESSON_NOT_FOUND",when:"Lesson ID is invalid"},NOT_ENROLLED:{description:"Not enrolled in course",http:403,gqlCode:"NOT_ENROLLED",when:"Learner is not enrolled in the course"}}},policy:{auth:"user"}}),N=l({meta:{key:"learning.submitCardReview",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","flashcards"],description:"Submit a flashcard review.",goal:"Record review and update SRS schedule.",context:"Called when reviewing flashcards."},io:{input:g,output:p,errors:{CARD_NOT_FOUND:{description:"Card does not exist",http:404,gqlCode:"CARD_NOT_FOUND",when:"Card ID is invalid"},INVALID_RATING:{description:"Invalid rating",http:400,gqlCode:"INVALID_RATING",when:"Rating must be AGAIN, HARD, GOOD, or EASY"}}},policy:{auth:"user"}}),m=x({meta:{key:"learning.getDueCards",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","flashcards"],description:"Get flashcards due for review.",goal:"Get the next batch of cards to review.",context:"Called when starting a review session."},io:{input:_,output:a},policy:{auth:"user"}}),S=x({meta:{key:"learning.getDashboard",version:"1.0.0",stability:"stable",owners:[...i],tags:["learning","dashboard"],description:"Get learner dashboard data.",goal:"Display learner progress and stats.",context:"Called when viewing the learning dashboard."},io:{input:I,output:c},policy:{auth:"user"}});export{p as SuccessOutput,g as SubmitCardReviewInput,N as SubmitCardReviewContract,$ as RecordJourneyEventContract,Q as ProgressModel,V as ListJourneyTracksContract,j as LearnerModel,c as LearnerDashboardModel,i as LEARNING_JOURNEY_OWNERS,v as JourneyTrackModel,o as JourneyProgressModel,I as GetLearnerDashboardInput,S as GetLearnerDashboardContract,Z as GetJourneyProgressContract,a as GetDueCardsOutput,_ as GetDueCardsInput,m as GetDueCardsContract,r as EnrollmentModel,O as EnrollInCourseInput,J as EnrollInCourseContract,U as DeckModel,G as CourseModel,d as CompleteLessonInput,R as CompleteLessonContract,h as CardModel,q as AchievementModel};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var q=["modules.learning-journey"];import{defineCommand as F,defineQuery as B}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as x,ScalarTypeEnum as j}from"@contractspec/lib.schema";var H=x({name:"JourneyCondition",description:"Adaptive completion condition for a journey step.",fields:{kind:{type:j.String_unsecure(),isOptional:!0},eventName:{type:j.String_unsecure(),isOptional:!1},eventVersion:{type:j.Int_unsecure(),isOptional:!0},sourceModule:{type:j.String_unsecure(),isOptional:!0},payloadFilter:{type:j.JSON(),isOptional:!0},atLeast:{type:j.Int_unsecure(),isOptional:!0},withinHours:{type:j.Int_unsecure(),isOptional:!0},withinHoursOfStart:{type:j.Int_unsecure(),isOptional:!0},availableAfterHours:{type:j.Int_unsecure(),isOptional:!0},skillIdField:{type:j.String_unsecure(),isOptional:!0},masteryField:{type:j.String_unsecure(),isOptional:!0},minimumMastery:{type:j.Float_unsecure(),isOptional:!0},requiredCount:{type:j.Int_unsecure(),isOptional:!0}}}),D=x({name:"JourneyReward",description:"XP and badge rewards for journey outcomes.",fields:{badgeKey:{type:j.String_unsecure(),isOptional:!0},xp:{type:j.Int_unsecure(),isOptional:!0}}}),K=x({name:"JourneyStep",description:"Adaptive learning journey step definition.",fields:{id:{type:j.String_unsecure(),isOptional:!1},title:{type:j.String_unsecure(),isOptional:!1},description:{type:j.String_unsecure(),isOptional:!0},instructions:{type:j.String_unsecure(),isOptional:!0},helpUrl:{type:j.String_unsecure(),isOptional:!0},order:{type:j.Int_unsecure(),isOptional:!0},completion:{type:H,isOptional:!1},availability:{type:j.JSON(),isOptional:!0},prerequisites:{type:j.JSON(),isOptional:!0},prerequisiteMode:{type:j.String_unsecure(),isOptional:!0},branches:{type:j.JSON(),isOptional:!0},reward:{type:D,isOptional:!0},xpReward:{type:j.Int_unsecure(),isOptional:!0},isRequired:{type:j.Boolean(),isOptional:!0},canSkip:{type:j.Boolean(),isOptional:!0},actionUrl:{type:j.String_unsecure(),isOptional:!0},actionLabel:{type:j.String_unsecure(),isOptional:!0},metadata:{type:j.JSON(),isOptional:!0}}}),V=x({name:"JourneyTrack",description:"Headless adaptive journey track definition.",fields:{id:{type:j.String_unsecure(),isOptional:!1},productId:{type:j.String_unsecure(),isOptional:!0},name:{type:j.String_unsecure(),isOptional:!1},description:{type:j.String_unsecure(),isOptional:!0},targetUserSegment:{type:j.String_unsecure(),isOptional:!0},targetRole:{type:j.String_unsecure(),isOptional:!0},totalXp:{type:j.Int_unsecure(),isOptional:!0},isActive:{type:j.Boolean(),isOptional:!0},isRequired:{type:j.Boolean(),isOptional:!0},canSkip:{type:j.Boolean(),isOptional:!0},streakRule:{type:j.JSON(),isOptional:!0},completionRewards:{type:D,isOptional:!0},metadata:{type:j.JSON(),isOptional:!0},steps:{type:K,isArray:!0,isOptional:!1}}}),X=x({name:"JourneyStepProgress",description:"Persisted and projected state for a journey step.",fields:{stepId:{type:j.String_unsecure(),isOptional:!1},status:{type:j.String_unsecure(),isOptional:!1},xpEarned:{type:j.Int_unsecure(),isOptional:!1},selectedBranchKey:{type:j.String_unsecure(),isOptional:!0},availableAt:{type:j.DateTime(),isOptional:!0},dueAt:{type:j.DateTime(),isOptional:!0},completedAt:{type:j.DateTime(),isOptional:!0},skippedAt:{type:j.DateTime(),isOptional:!0},blockedAt:{type:j.DateTime(),isOptional:!0},missedAt:{type:j.DateTime(),isOptional:!0},triggeringEvent:{type:j.String_unsecure(),isOptional:!0},eventPayload:{type:j.JSON(),isOptional:!0},occurrences:{type:j.Int_unsecure(),isOptional:!0},masteryCount:{type:j.Int_unsecure(),isOptional:!0}}}),z=x({name:"JourneyProgress",description:"Projected adaptive journey progress snapshot.",fields:{learnerId:{type:j.String_unsecure(),isOptional:!0},trackId:{type:j.String_unsecure(),isOptional:!1},progressPercent:{type:j.Int_unsecure(),isOptional:!1},isCompleted:{type:j.Boolean(),isOptional:!1},xpEarned:{type:j.Int_unsecure(),isOptional:!1},streakDays:{type:j.Int_unsecure(),isOptional:!1},badges:{type:j.String_unsecure(),isArray:!0,isOptional:!1},activeStepCount:{type:j.Int_unsecure(),isOptional:!1},completedStepCount:{type:j.Int_unsecure(),isOptional:!1},nextStepId:{type:j.String_unsecure(),isOptional:!0},currentStepId:{type:j.String_unsecure(),isOptional:!0},startedAt:{type:j.DateTime(),isOptional:!0},completedAt:{type:j.DateTime(),isOptional:!0},lastActivityAt:{type:j.DateTime(),isOptional:!0},steps:{type:X,isArray:!0,isOptional:!1}}}),Z=x({name:"JourneyTrackListInput",description:"Filters for listing adaptive journey tracks.",fields:{learnerId:{type:j.String_unsecure(),isOptional:!0},productId:{type:j.String_unsecure(),isOptional:!0},trackIds:{type:j.String_unsecure(),isArray:!0,isOptional:!0},includeProgress:{type:j.Boolean(),isOptional:!0}}}),$=x({name:"JourneyTrackListOutput",description:"Adaptive journey catalog with optional progress snapshots.",fields:{tracks:{type:V,isArray:!0,isOptional:!1},progress:{type:z,isArray:!0,isOptional:!0}}}),v=x({name:"JourneyProgressInput",description:"Input for fetching projected progress for one track.",fields:{learnerId:{type:j.String_unsecure(),isOptional:!0},trackId:{type:j.String_unsecure(),isOptional:!1}}}),b=x({name:"JourneyRecordEventInput",description:"Domain event used to advance an adaptive journey.",fields:{learnerId:{type:j.String_unsecure(),isOptional:!1},trackId:{type:j.String_unsecure(),isOptional:!0},eventName:{type:j.String_unsecure(),isOptional:!1},eventVersion:{type:j.Int_unsecure(),isOptional:!0},sourceModule:{type:j.String_unsecure(),isOptional:!0},eventPayload:{type:j.JSON(),isOptional:!0},occurredAt:{type:j.DateTime(),isOptional:!0}}}),Q=B({meta:{key:"learning.journey.listTracks",version:"1.0.0",stability:"stable",owners:[...q],tags:["adaptive-learning","journey","learning"],description:"List adaptive learning journeys for a learner or product.",goal:"Expose the adaptive journey catalog to UI and API surfaces.",context:"Called when rendering journey lists, launchers, or registries."},io:{input:Z,output:$},policy:{auth:"user"}}),U=B({meta:{key:"learning.journey.getProgress",version:"1.0.0",stability:"stable",owners:[...q],tags:["adaptive-learning","journey","progress"],description:"Project the latest adaptive journey progress for one track.",goal:"Render next-step, branch, and reward state consistently.",context:"Called by widgets, journey detail pages, and adaptive shells."},io:{input:v,output:z},policy:{auth:"user"}}),W=F({meta:{key:"learning.journey.recordEvent",version:"1.0.0",stability:"stable",owners:[...q],tags:["adaptive-learning","journey","events"],description:"Record a domain event and re-evaluate an adaptive journey.",goal:"Advance branch-aware journey progress from product activity.",context:"Called by event handlers when product activity may unlock or complete steps."},io:{input:b,output:z},policy:{auth:"user"}});export{W as RecordJourneyEventContract,Q as ListJourneyTracksContract,V as JourneyTrackModel,z as JourneyProgressModel,U as GetJourneyProgressContract};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineSchemaModel as t,ScalarTypeEnum as e}from"@contractspec/lib.schema";var
|
|
1
|
+
var s=["modules.learning-journey"];import{defineSchemaModel as t,ScalarTypeEnum as e}from"@contractspec/lib.schema";var S=t({name:"Course",description:"A learning course",fields:{id:{type:e.String_unsecure(),isOptional:!1},title:{type:e.String_unsecure(),isOptional:!1},slug:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},difficulty:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},estimatedDuration:{type:e.Int_unsecure(),isOptional:!0},thumbnailUrl:{type:e.String_unsecure(),isOptional:!0},createdAt:{type:e.DateTime(),isOptional:!1}}}),f=t({name:"Learner",description:"A learner profile",fields:{id:{type:e.String_unsecure(),isOptional:!1},userId:{type:e.String_unsecure(),isOptional:!1},displayName:{type:e.String_unsecure(),isOptional:!0},level:{type:e.Int_unsecure(),isOptional:!1},totalXp:{type:e.Int_unsecure(),isOptional:!1},currentStreak:{type:e.Int_unsecure(),isOptional:!1},longestStreak:{type:e.Int_unsecure(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),n=t({name:"Enrollment",description:"A course enrollment",fields:{id:{type:e.String_unsecure(),isOptional:!1},learnerId:{type:e.String_unsecure(),isOptional:!1},courseId:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},progress:{type:e.Int_unsecure(),isOptional:!1},startedAt:{type:e.DateTime(),isOptional:!0},completedAt:{type:e.DateTime(),isOptional:!0}}}),I=t({name:"LessonProgress",description:"Lesson progress",fields:{id:{type:e.String_unsecure(),isOptional:!1},learnerId:{type:e.String_unsecure(),isOptional:!1},lessonId:{type:e.String_unsecure(),isOptional:!1},status:{type:e.String_unsecure(),isOptional:!1},progress:{type:e.Int_unsecure(),isOptional:!1},score:{type:e.Int_unsecure(),isOptional:!0},timeSpent:{type:e.Int_unsecure(),isOptional:!1},completedAt:{type:e.DateTime(),isOptional:!0}}}),x=t({name:"Deck",description:"A flashcard deck",fields:{id:{type:e.String_unsecure(),isOptional:!1},title:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},cardCount:{type:e.Int_unsecure(),isOptional:!1},isPublic:{type:e.Boolean(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),O=t({name:"Card",description:"A flashcard",fields:{id:{type:e.String_unsecure(),isOptional:!1},deckId:{type:e.String_unsecure(),isOptional:!1},front:{type:e.String_unsecure(),isOptional:!1},back:{type:e.String_unsecure(),isOptional:!1},hints:{type:e.JSON(),isOptional:!0},isDue:{type:e.Boolean(),isOptional:!1},nextReviewAt:{type:e.DateTime(),isOptional:!0}}}),g=t({name:"Achievement",description:"An achievement",fields:{id:{type:e.String_unsecure(),isOptional:!1},key:{type:e.String_unsecure(),isOptional:!1},name:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!1},icon:{type:e.String_unsecure(),isOptional:!0},xpReward:{type:e.Int_unsecure(),isOptional:!1},unlockedAt:{type:e.DateTime(),isOptional:!0}}}),a=t({name:"EnrollInCourseInput",description:"Input for enrolling in a course",fields:{courseId:{type:e.String_unsecure(),isOptional:!1}}}),p=t({name:"CompleteLessonInput",description:"Input for completing a lesson",fields:{lessonId:{type:e.String_unsecure(),isOptional:!1},score:{type:e.Int_unsecure(),isOptional:!0},timeSpent:{type:e.Int_unsecure(),isOptional:!1}}}),l=t({name:"SubmitCardReviewInput",description:"Input for submitting a card review",fields:{cardId:{type:e.String_unsecure(),isOptional:!1},rating:{type:e.String_unsecure(),isOptional:!1},responseTimeMs:{type:e.Int_unsecure(),isOptional:!0}}}),o=t({name:"GetDueCardsInput",description:"Input for getting due cards",fields:{deckId:{type:e.String_unsecure(),isOptional:!0},limit:{type:e.Int_unsecure(),isOptional:!0}}}),u=t({name:"GetDueCardsOutput",description:"Output for getting due cards",fields:{cards:{type:O,isArray:!0,isOptional:!1},total:{type:e.Int_unsecure(),isOptional:!1}}}),c=t({name:"GetLearnerDashboardInput",description:"Input for getting learner dashboard",fields:{learnerId:{type:e.String_unsecure(),isOptional:!0}}}),d=t({name:"LearnerDashboard",description:"Learner dashboard data",fields:{learner:{type:f,isOptional:!1},currentStreak:{type:e.Int_unsecure(),isOptional:!1},dailyXpGoal:{type:e.Int_unsecure(),isOptional:!1},dailyXpProgress:{type:e.Int_unsecure(),isOptional:!1},activeEnrollments:{type:n,isArray:!0,isOptional:!1},recentAchievements:{type:g,isArray:!0,isOptional:!1},dueCardCount:{type:e.Int_unsecure(),isOptional:!1}}}),i=t({name:"SuccessOutput",description:"Generic success output",fields:{success:{type:e.Boolean(),isOptional:!1},xpEarned:{type:e.Int_unsecure(),isOptional:!0}}});import{defineCommand as r,defineQuery as y}from"@contractspec/lib.contracts-spec";var T=r({meta:{key:"learning.enroll",version:"1.0.0",stability:"stable",owners:[...s],tags:["learning","enrollment"],description:"Enroll in a course.",goal:"Start learning a new course.",context:"Called when a learner wants to start a course."},io:{input:a,output:n,errors:{COURSE_NOT_FOUND:{description:"Course does not exist",http:404,gqlCode:"COURSE_NOT_FOUND",when:"Course ID is invalid"},ALREADY_ENROLLED:{description:"Already enrolled in course",http:409,gqlCode:"ALREADY_ENROLLED",when:"Learner is already enrolled"}}},policy:{auth:"user"}}),v=r({meta:{key:"learning.completeLesson",version:"1.0.0",stability:"stable",owners:[...s],tags:["learning","progress"],description:"Mark a lesson as completed.",goal:"Record lesson completion and earn XP.",context:"Called when a learner finishes a lesson."},io:{input:p,output:i,errors:{LESSON_NOT_FOUND:{description:"Lesson does not exist",http:404,gqlCode:"LESSON_NOT_FOUND",when:"Lesson ID is invalid"},NOT_ENROLLED:{description:"Not enrolled in course",http:403,gqlCode:"NOT_ENROLLED",when:"Learner is not enrolled in the course"}}},policy:{auth:"user"}}),C=r({meta:{key:"learning.submitCardReview",version:"1.0.0",stability:"stable",owners:[...s],tags:["learning","flashcards"],description:"Submit a flashcard review.",goal:"Record review and update SRS schedule.",context:"Called when reviewing flashcards."},io:{input:l,output:i,errors:{CARD_NOT_FOUND:{description:"Card does not exist",http:404,gqlCode:"CARD_NOT_FOUND",when:"Card ID is invalid"},INVALID_RATING:{description:"Invalid rating",http:400,gqlCode:"INVALID_RATING",when:"Rating must be AGAIN, HARD, GOOD, or EASY"}}},policy:{auth:"user"}}),b=y({meta:{key:"learning.getDueCards",version:"1.0.0",stability:"stable",owners:[...s],tags:["learning","flashcards"],description:"Get flashcards due for review.",goal:"Get the next batch of cards to review.",context:"Called when starting a review session."},io:{input:o,output:u},policy:{auth:"user"}}),M=y({meta:{key:"learning.getDashboard",version:"1.0.0",stability:"stable",owners:[...s],tags:["learning","dashboard"],description:"Get learner dashboard data.",goal:"Display learner progress and stats.",context:"Called when viewing the learning dashboard."},io:{input:c,output:d},policy:{auth:"user"}});export{C as SubmitCardReviewContract,M as GetLearnerDashboardContract,b as GetDueCardsContract,T as EnrollInCourseContract,v as CompleteLessonContract};
|
package/dist/node/docs/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import{registerDocBlocks as m}from"@contractspec/lib.contracts-spec/docs";var f=[{id:"docs.learning-journey.engine",title:"Learning Journey Engine",summary:"
|
|
1
|
+
import{registerDocBlocks as m}from"@contractspec/lib.contracts-spec/docs";var f=[{id:"docs.learning-journey.engine",title:"Learning Journey Engine",summary:"Headless adaptive journey runtime for tracks, branching steps, progress, quizzes, streaks, XP, and AI coaching hooks.",kind:"reference",visibility:"public",route:"/docs/learning-journey/engine",tags:["adaptive-learning","journey","education"],body:`## Capabilities
|
|
2
2
|
|
|
3
3
|
- **Entities**: Learner, Track, Module, Step, Progress, Quiz, Flashcard, AI Coach, Gamification (XP, streaks, badges).
|
|
4
|
-
- **Contracts**: enroll/resume/advance steps, complete quizzes, record streaks, assign XP, fetch progress dashboards,
|
|
4
|
+
- **Contracts**: enroll/resume/advance steps, complete quizzes, record streaks, assign XP, fetch progress dashboards, journey list/progress/recordEvent.
|
|
5
|
+
- **Runtime**: canonical branch/prerequisite evaluator + projected next-step snapshots.
|
|
5
6
|
- **Engines**: spaced-repetition (SRS), streak calculator, XP progression.
|
|
6
|
-
- **Events**: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded,
|
|
7
|
+
- **Events**: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded, journey.started/completed/step_completed.
|
|
7
8
|
|
|
8
9
|
## Completion conditions
|
|
9
10
|
- Event-based: name/version/source + payload filter
|
|
10
11
|
- Count-based: require N events (optional time window)
|
|
11
12
|
- Time-bounded: must complete within a window; steps can unlock by day/hour
|
|
12
|
-
-
|
|
13
|
+
- Mastery: complete when cards/skills hit mastery thresholds (with required counts)
|
|
13
14
|
|
|
14
15
|
## Usage
|
|
15
16
|
|
|
@@ -20,7 +21,7 @@ import{registerDocBlocks as m}from"@contractspec/lib.contracts-spec/docs";var f=
|
|
|
20
21
|
- Import from \`@contractspec/module.learning-journey\` into your spec registry.
|
|
21
22
|
|
|
22
23
|
3) Bind to product actions
|
|
23
|
-
- Tie \`
|
|
24
|
+
- Tie \`JourneyStep\` completion conditions and branch rules to domain events (e.g., deal.created, agent.run.completed, drill.session.completed).
|
|
24
25
|
- Trigger notifications via Notification Center and audits on completion.
|
|
25
26
|
|
|
26
27
|
4) Gamification
|
|
@@ -41,10 +42,10 @@ const updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });
|
|
|
41
42
|
- Keep steps bound to real product events, not just button clicks.
|
|
42
43
|
- Avoid storing PII in content; keep org/user scoping for multi-tenant isolation.
|
|
43
44
|
- Emit analytics and audit logs for completions; respect \`prefers-reduced-motion\` in UIs consuming these specs.
|
|
44
|
-
- Track completion bonuses: \`
|
|
45
|
+
- Track completion bonuses: \`completionRewards\`, optional \`streakRule\`, and branch-level rewards.
|
|
45
46
|
`},{id:"docs.learning-journey.goal",title:"Learning Journey — Goal",summary:"Why the learning journey engine exists and the outcomes it targets.",kind:"goal",visibility:"public",route:"/docs/learning-journey/goal",tags:["learning","goal"],body:`## Why it matters
|
|
46
|
-
- Provides a regenerable
|
|
47
|
-
- Keeps tracks, steps, quizzes, and gamification consistent across surfaces.
|
|
47
|
+
- Provides a regenerable adaptive-journey engine tied to product signals.
|
|
48
|
+
- Keeps tracks, steps, branches, quizzes, and gamification consistent across surfaces.
|
|
48
49
|
|
|
49
50
|
## Business/Product goal
|
|
50
51
|
- Drive activation and retention with measurable progress, SRS, and streaks.
|
|
@@ -55,18 +56,18 @@ const updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });
|
|
|
55
56
|
- Analytics/audit hooks exist for completions and streaks.`},{id:"docs.learning-journey.usage",title:"Learning Journey — Usage",summary:"How to compose, bind, and regenerate journeys safely.",kind:"usage",visibility:"public",route:"/docs/learning-journey/usage",tags:["learning","usage"],body:`## Setup
|
|
56
57
|
1) Include \`learningJourneyEntities\` in schema composition.
|
|
57
58
|
2) Register contracts/events from \`@contractspec/module.learning-journey\`.
|
|
58
|
-
3) Bind steps to real product events (e.g., deal.created, run.completed).
|
|
59
|
+
3) Bind steps and branch outcomes to real product events (e.g., deal.created, run.completed).
|
|
59
60
|
|
|
60
61
|
## Extend & regenerate
|
|
61
|
-
1) Update track/module/step definitions or quiz schemas in spec.
|
|
62
|
+
1) Update track/module/step definitions, branch rules, or quiz schemas in spec.
|
|
62
63
|
2) Regenerate to sync UI/API/events; mark PII paths where needed.
|
|
63
64
|
3) Use Feature Flags to trial new tracks or streak rules.
|
|
64
65
|
|
|
65
66
|
## Guardrails
|
|
66
|
-
- Avoid hardcoded progression; keep
|
|
67
|
+
- Avoid hardcoded progression; keep runtime rules declarative.
|
|
67
68
|
- Emit analytics/audit for completions; respect user locale/accessibility in presentations.
|
|
68
69
|
- Keep content free of PII; scope learners by org/tenant.`},{id:"docs.learning-journey.constraints",title:"Learning Journey — Constraints & Safety",summary:"Internal guardrails for progression, telemetry, and regeneration semantics.",kind:"reference",visibility:"internal",route:"/docs/learning-journey/constraints",tags:["learning","constraints","internal"],body:`## Constraints
|
|
69
|
-
- Progression (tracks/modules/steps) and engines (SRS, streaks, XP) must stay declarative in spec.
|
|
70
|
+
- Progression (tracks/modules/steps/branches) and engines (SRS, streaks, XP) must stay declarative in spec.
|
|
70
71
|
- Events to emit: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded.
|
|
71
72
|
- Regeneration should not change scoring/streak rules without explicit spec change.
|
|
72
73
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import{registerDocBlocks as f}from"@contractspec/lib.contracts-spec/docs";var h=[{id:"docs.learning-journey.engine",title:"Learning Journey Engine",summary:"
|
|
1
|
+
import{registerDocBlocks as f}from"@contractspec/lib.contracts-spec/docs";var h=[{id:"docs.learning-journey.engine",title:"Learning Journey Engine",summary:"Headless adaptive journey runtime for tracks, branching steps, progress, quizzes, streaks, XP, and AI coaching hooks.",kind:"reference",visibility:"public",route:"/docs/learning-journey/engine",tags:["adaptive-learning","journey","education"],body:`## Capabilities
|
|
2
2
|
|
|
3
3
|
- **Entities**: Learner, Track, Module, Step, Progress, Quiz, Flashcard, AI Coach, Gamification (XP, streaks, badges).
|
|
4
|
-
- **Contracts**: enroll/resume/advance steps, complete quizzes, record streaks, assign XP, fetch progress dashboards,
|
|
4
|
+
- **Contracts**: enroll/resume/advance steps, complete quizzes, record streaks, assign XP, fetch progress dashboards, journey list/progress/recordEvent.
|
|
5
|
+
- **Runtime**: canonical branch/prerequisite evaluator + projected next-step snapshots.
|
|
5
6
|
- **Engines**: spaced-repetition (SRS), streak calculator, XP progression.
|
|
6
|
-
- **Events**: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded,
|
|
7
|
+
- **Events**: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded, journey.started/completed/step_completed.
|
|
7
8
|
|
|
8
9
|
## Completion conditions
|
|
9
10
|
- Event-based: name/version/source + payload filter
|
|
10
11
|
- Count-based: require N events (optional time window)
|
|
11
12
|
- Time-bounded: must complete within a window; steps can unlock by day/hour
|
|
12
|
-
-
|
|
13
|
+
- Mastery: complete when cards/skills hit mastery thresholds (with required counts)
|
|
13
14
|
|
|
14
15
|
## Usage
|
|
15
16
|
|
|
@@ -20,7 +21,7 @@ import{registerDocBlocks as f}from"@contractspec/lib.contracts-spec/docs";var h=
|
|
|
20
21
|
- Import from \`@contractspec/module.learning-journey\` into your spec registry.
|
|
21
22
|
|
|
22
23
|
3) Bind to product actions
|
|
23
|
-
- Tie \`
|
|
24
|
+
- Tie \`JourneyStep\` completion conditions and branch rules to domain events (e.g., deal.created, agent.run.completed, drill.session.completed).
|
|
24
25
|
- Trigger notifications via Notification Center and audits on completion.
|
|
25
26
|
|
|
26
27
|
4) Gamification
|
|
@@ -41,10 +42,10 @@ const updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });
|
|
|
41
42
|
- Keep steps bound to real product events, not just button clicks.
|
|
42
43
|
- Avoid storing PII in content; keep org/user scoping for multi-tenant isolation.
|
|
43
44
|
- Emit analytics and audit logs for completions; respect \`prefers-reduced-motion\` in UIs consuming these specs.
|
|
44
|
-
- Track completion bonuses: \`
|
|
45
|
+
- Track completion bonuses: \`completionRewards\`, optional \`streakRule\`, and branch-level rewards.
|
|
45
46
|
`},{id:"docs.learning-journey.goal",title:"Learning Journey — Goal",summary:"Why the learning journey engine exists and the outcomes it targets.",kind:"goal",visibility:"public",route:"/docs/learning-journey/goal",tags:["learning","goal"],body:`## Why it matters
|
|
46
|
-
- Provides a regenerable
|
|
47
|
-
- Keeps tracks, steps, quizzes, and gamification consistent across surfaces.
|
|
47
|
+
- Provides a regenerable adaptive-journey engine tied to product signals.
|
|
48
|
+
- Keeps tracks, steps, branches, quizzes, and gamification consistent across surfaces.
|
|
48
49
|
|
|
49
50
|
## Business/Product goal
|
|
50
51
|
- Drive activation and retention with measurable progress, SRS, and streaks.
|
|
@@ -55,18 +56,18 @@ const updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });
|
|
|
55
56
|
- Analytics/audit hooks exist for completions and streaks.`},{id:"docs.learning-journey.usage",title:"Learning Journey — Usage",summary:"How to compose, bind, and regenerate journeys safely.",kind:"usage",visibility:"public",route:"/docs/learning-journey/usage",tags:["learning","usage"],body:`## Setup
|
|
56
57
|
1) Include \`learningJourneyEntities\` in schema composition.
|
|
57
58
|
2) Register contracts/events from \`@contractspec/module.learning-journey\`.
|
|
58
|
-
3) Bind steps to real product events (e.g., deal.created, run.completed).
|
|
59
|
+
3) Bind steps and branch outcomes to real product events (e.g., deal.created, run.completed).
|
|
59
60
|
|
|
60
61
|
## Extend & regenerate
|
|
61
|
-
1) Update track/module/step definitions or quiz schemas in spec.
|
|
62
|
+
1) Update track/module/step definitions, branch rules, or quiz schemas in spec.
|
|
62
63
|
2) Regenerate to sync UI/API/events; mark PII paths where needed.
|
|
63
64
|
3) Use Feature Flags to trial new tracks or streak rules.
|
|
64
65
|
|
|
65
66
|
## Guardrails
|
|
66
|
-
- Avoid hardcoded progression; keep
|
|
67
|
+
- Avoid hardcoded progression; keep runtime rules declarative.
|
|
67
68
|
- Emit analytics/audit for completions; respect user locale/accessibility in presentations.
|
|
68
69
|
- Keep content free of PII; scope learners by org/tenant.`},{id:"docs.learning-journey.constraints",title:"Learning Journey — Constraints & Safety",summary:"Internal guardrails for progression, telemetry, and regeneration semantics.",kind:"reference",visibility:"internal",route:"/docs/learning-journey/constraints",tags:["learning","constraints","internal"],body:`## Constraints
|
|
69
|
-
- Progression (tracks/modules/steps) and engines (SRS, streaks, XP) must stay declarative in spec.
|
|
70
|
+
- Progression (tracks/modules/steps/branches) and engines (SRS, streaks, XP) must stay declarative in spec.
|
|
70
71
|
- Events to emit: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded.
|
|
71
72
|
- Regeneration should not change scoring/streak rules without explicit spec change.
|
|
72
73
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var N={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 K{config;constructor(j={}){this.config={...N,...j}}calculateNextReview(j,Q,W=new Date){if(!j.isGraduated&&!j.isRelearning)return this.handleLearningCard(j,Q,W);if(j.isRelearning)return this.handleRelearningCard(j,Q,W);return this.handleReviewCard(j,Q,W)}getInitialState(){return{interval:0,easeFactor:2.5,repetitions:0,learningStep:0,isGraduated:!1,isRelearning:!1,lapses:0}}isDue(j,Q=new Date){return j<=Q}getOverdueDays(j,Q=new Date){let W=Q.getTime()-j.getTime();return Math.floor(W/86400000)}handleLearningCard(j,Q,W){let H=this.config.learningSteps,V=j.learningStep,Z=!1,$=0,J;switch(Q){case"AGAIN":V=0,$=H[0]??1,J=this.addMinutes(W,$);break;case"HARD":$=H[V]??H[0]??1,J=this.addMinutes(W,$);break;case"GOOD":if(V++,V>=H.length)Z=!0,$=this.config.graduatingInterval,J=this.addDays(W,$);else $=H[V]??10,J=this.addMinutes(W,$);break;case"EASY":Z=!0,$=this.config.easyInterval,J=this.addDays(W,$);break}return{interval:Z?$:0,easeFactor:j.easeFactor,repetitions:Z?1:0,nextReviewAt:J,learningStep:V,isGraduated:Z,isRelearning:!1,lapses:j.lapses}}handleRelearningCard(j,Q,W){let H=this.config.relearningSteps,V=j.learningStep,Z=!0,$=0,J;switch(Q){case"AGAIN":V=0,$=H[0]??10,J=this.addMinutes(W,$);break;case"HARD":$=H[V]??H[0]??10,J=this.addMinutes(W,$);break;case"GOOD":if(V++,V>=H.length)Z=!1,$=Math.max(1,Math.floor(j.interval*this.config.newIntervalModifier)),J=this.addDays(W,$);else $=H[V]??10,J=this.addMinutes(W,$);break;case"EASY":Z=!1,$=Math.max(1,Math.floor(j.interval*this.config.newIntervalModifier*1.5)),J=this.addDays(W,$);break}return{interval:Z?j.interval:$,easeFactor:j.easeFactor,repetitions:Z?j.repetitions:j.repetitions+1,nextReviewAt:J,learningStep:V,isGraduated:!0,isRelearning:Z,lapses:j.lapses}}handleReviewCard(j,Q,W){let H,V=j.easeFactor,Z=j.repetitions,$=!1,J=0,q=j.lapses;switch(Q){case"AGAIN":return q++,$=!0,J=0,V=Math.max(this.config.minEaseFactor,V-0.2),H=j.interval,{interval:H,easeFactor:V,repetitions:Z,nextReviewAt:this.addMinutes(W,this.config.relearningSteps[0]??10),learningStep:J,isGraduated:!0,isRelearning:!0,lapses:q};case"HARD":V=Math.max(this.config.minEaseFactor,V-0.15),H=Math.max(j.interval+1,j.interval*this.config.hardIntervalModifier);break;case"GOOD":H=j.interval*V*this.config.intervalModifier,Z++;break;case"EASY":V=V+0.15,H=j.interval*V*this.config.easyBonus*this.config.intervalModifier,Z++;break}return H=Math.min(Math.round(H),this.config.maxInterval),H=Math.max(1,H),{interval:H,easeFactor:V,repetitions:Z,nextReviewAt:this.addDays(W,H),learningStep:J,isGraduated:!0,isRelearning:$,lapses:q}}addMinutes(j,Q){return new Date(j.getTime()+Q*60*1000)}addDays(j,Q){return new Date(j.getTime()+Q*24*60*60*1000)}}var I=new K;var O={timezone:"UTC",freezesPerMonth:2,maxFreezes:5,gracePeriodHours:4};class h{config;constructor(j={}){this.config={...O,...j}}update(j,Q=new Date){let W=this.getDateString(Q),H={state:{...j},streakMaintained:!1,streakLost:!1,freezeUsed:!1,newStreak:!1,daysMissed:0};if(!j.lastActivityDate)return H.state.currentStreak=1,H.state.longestStreak=Math.max(1,j.longestStreak),H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.newStreak=!0,H.streakMaintained=!0,H;if(j.lastActivityDate===W)return H.state.lastActivityAt=Q,H.streakMaintained=!0,H;let V=this.getDaysBetween(j.lastActivityDate,W);if(V===1)return H.state.currentStreak=j.currentStreak+1,H.state.longestStreak=Math.max(H.state.currentStreak,j.longestStreak),H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.streakMaintained=!0,H;H.daysMissed=V-1;let Z=H.daysMissed;if(Z<=j.freezesRemaining)return H.state.freezesRemaining=j.freezesRemaining-Z,H.state.freezeUsedAt=Q,H.state.currentStreak=j.currentStreak+1,H.state.longestStreak=Math.max(H.state.currentStreak,j.longestStreak),H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.freezeUsed=!0,H.streakMaintained=!0,H;return H.streakLost=!0,H.state.currentStreak=1,H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.newStreak=!0,H}checkStatus(j,Q=new Date){if(!j.lastActivityDate)return{isActive:!1,willExpireAt:null,canUseFreeze:!1,daysUntilExpiry:0};let W=this.getDateString(Q),H=this.getDaysBetween(j.lastActivityDate,W);if(H===0){let Z=this.addDays(Q,1);return Z.setHours(23,59,59,999),{isActive:!0,willExpireAt:Z,canUseFreeze:j.freezesRemaining>0,daysUntilExpiry:1}}if(H===1){let Z=new Date(Q);return Z.setHours(23+this.config.gracePeriodHours,59,59,999),{isActive:!0,willExpireAt:Z,canUseFreeze:j.freezesRemaining>0,daysUntilExpiry:0}}let V=H-1;return{isActive:V<=j.freezesRemaining,willExpireAt:null,canUseFreeze:V<=j.freezesRemaining,daysUntilExpiry:-V}}useFreeze(j,Q=new Date){if(j.freezesRemaining<=0)return null;return{...j,freezesRemaining:j.freezesRemaining-1,freezeUsedAt:Q}}awardMonthlyFreezes(j){return{...j,freezesRemaining:Math.min(j.freezesRemaining+this.config.freezesPerMonth,this.config.maxFreezes)}}getInitialState(){return{currentStreak:0,longestStreak:0,lastActivityAt:null,lastActivityDate:null,freezesRemaining:this.config.freezesPerMonth,freezeUsedAt:null}}getMilestones(j){let Q=[3,7,14,30,60,90,180,365,500,1000],W=Q.filter((V)=>j>=V),H=Q.find((V)=>j<V)??null;return{achieved:W,next:H}}getDateString(j){let Q=j.getFullYear(),W=String(j.getMonth()+1).padStart(2,"0"),H=String(j.getDate()).padStart(2,"0");return`${Q}-${W}-${H}`}getDaysBetween(j,Q){let W=new Date(j),V=new Date(Q).getTime()-W.getTime();return Math.floor(V/86400000)}addDays(j,Q){return new Date(j.getTime()+Q*24*60*60*1000)}}var x=new h;import{defineTranslation as L}from"@contractspec/lib.contracts-spec/translations";var B=L({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 G}from"@contractspec/lib.contracts-spec/translations";var M=G({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 U=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 m}from"@contractspec/lib.contracts-spec/translations";var z=m({specKey:"learning-journey.messages",catalogs:[B,U,M]}),P=z.create,v=z.getDefault,d=z.resetRegistry;var Y={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,onboarding_step:5,onboarding_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 C{config;constructor(j={}){this.config={...Y,...j,baseValues:{...Y.baseValues,...j.baseValues},scoreThresholds:j.scoreThresholds||Y.scoreThresholds,streakTiers:j.streakTiers||Y.streakTiers}}calculate(j){let Q=[],W=j.baseXp??this.config.baseValues[j.activity],H=W;if(Q.push({source:"base",amount:W}),j.score!==void 0){let V=this.getScoreMultiplier(j.score);if(V!==1){let Z=Math.round(W*(V-1));H+=Z,Q.push({source:"score_bonus",amount:Z,multiplier:V})}if(j.score===100){let Z=Math.round(W*(this.config.perfectScoreMultiplier-1));H+=Z,Q.push({source:"perfect_score",amount:Z,multiplier:this.config.perfectScoreMultiplier})}}if(j.attemptNumber===1&&!j.isRetry)H+=this.config.firstAttemptBonus,Q.push({source:"first_attempt",amount:this.config.firstAttemptBonus});if(j.isRetry){let V=Math.round(H*(1-this.config.retryPenalty));H-=V,Q.push({source:"retry_penalty",amount:-V,multiplier:this.config.retryPenalty})}if(j.currentStreak&&j.currentStreak>0){let V=this.getStreakBonus(j.currentStreak);if(V>0)H+=V,Q.push({source:"streak_bonus",amount:V})}if(W>0)H=Math.max(1,H);return{totalXp:Math.round(H),baseXp:W,breakdown:Q}}calculateStreakBonus(j){let Q=this.getStreakBonus(j);return{totalXp:Q,baseXp:Q,breakdown:[{source:"streak_bonus",amount:Q}]}}getXpForLevel(j){if(j<=1)return 0;return Math.round(100*Math.pow(j-1,1.5))}getLevelFromXp(j){let Q=1,W=this.getXpForLevel(Q+1);while(j>=W&&Q<1000)Q++,W=this.getXpForLevel(Q+1);let H=this.getXpForLevel(Q),V=this.getXpForLevel(Q+1);return{level:Q,xpInLevel:j-H,xpForNextLevel:V-H}}getScoreMultiplier(j){for(let Q of this.config.scoreThresholds)if(j>=Q.min)return Q.multiplier;return 1}getStreakBonus(j){for(let Q of this.config.streakTiers)if(j>=Q.days)return Q.bonus;return 0}}var _={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 u(j,Q){let W=P(Q),H=_[j];return H?W.t(H):j}var w=new C;export{w as xpEngine,x as streakEngine,I as srsEngine,u as getXpSourceLabel,C as XPEngine,h as StreakEngine,K as SRSEngine,Y as DEFAULT_XP_CONFIG,O as DEFAULT_STREAK_CONFIG,N as DEFAULT_SRS_CONFIG};
|
|
1
|
+
var N={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 K{config;constructor(j={}){this.config={...N,...j}}calculateNextReview(j,Q,W=new Date){if(!j.isGraduated&&!j.isRelearning)return this.handleLearningCard(j,Q,W);if(j.isRelearning)return this.handleRelearningCard(j,Q,W);return this.handleReviewCard(j,Q,W)}getInitialState(){return{interval:0,easeFactor:2.5,repetitions:0,learningStep:0,isGraduated:!1,isRelearning:!1,lapses:0}}isDue(j,Q=new Date){return j<=Q}getOverdueDays(j,Q=new Date){let W=Q.getTime()-j.getTime();return Math.floor(W/86400000)}handleLearningCard(j,Q,W){let H=this.config.learningSteps,V=j.learningStep,Z=!1,$=0,J;switch(Q){case"AGAIN":V=0,$=H[0]??1,J=this.addMinutes(W,$);break;case"HARD":$=H[V]??H[0]??1,J=this.addMinutes(W,$);break;case"GOOD":if(V++,V>=H.length)Z=!0,$=this.config.graduatingInterval,J=this.addDays(W,$);else $=H[V]??10,J=this.addMinutes(W,$);break;case"EASY":Z=!0,$=this.config.easyInterval,J=this.addDays(W,$);break}return{interval:Z?$:0,easeFactor:j.easeFactor,repetitions:Z?1:0,nextReviewAt:J,learningStep:V,isGraduated:Z,isRelearning:!1,lapses:j.lapses}}handleRelearningCard(j,Q,W){let H=this.config.relearningSteps,V=j.learningStep,Z=!0,$=0,J;switch(Q){case"AGAIN":V=0,$=H[0]??10,J=this.addMinutes(W,$);break;case"HARD":$=H[V]??H[0]??10,J=this.addMinutes(W,$);break;case"GOOD":if(V++,V>=H.length)Z=!1,$=Math.max(1,Math.floor(j.interval*this.config.newIntervalModifier)),J=this.addDays(W,$);else $=H[V]??10,J=this.addMinutes(W,$);break;case"EASY":Z=!1,$=Math.max(1,Math.floor(j.interval*this.config.newIntervalModifier*1.5)),J=this.addDays(W,$);break}return{interval:Z?j.interval:$,easeFactor:j.easeFactor,repetitions:Z?j.repetitions:j.repetitions+1,nextReviewAt:J,learningStep:V,isGraduated:!0,isRelearning:Z,lapses:j.lapses}}handleReviewCard(j,Q,W){let H,V=j.easeFactor,Z=j.repetitions,$=!1,J=0,q=j.lapses;switch(Q){case"AGAIN":return q++,$=!0,J=0,V=Math.max(this.config.minEaseFactor,V-0.2),H=j.interval,{interval:H,easeFactor:V,repetitions:Z,nextReviewAt:this.addMinutes(W,this.config.relearningSteps[0]??10),learningStep:J,isGraduated:!0,isRelearning:!0,lapses:q};case"HARD":V=Math.max(this.config.minEaseFactor,V-0.15),H=Math.max(j.interval+1,j.interval*this.config.hardIntervalModifier);break;case"GOOD":H=j.interval*V*this.config.intervalModifier,Z++;break;case"EASY":V=V+0.15,H=j.interval*V*this.config.easyBonus*this.config.intervalModifier,Z++;break}return H=Math.min(Math.round(H),this.config.maxInterval),H=Math.max(1,H),{interval:H,easeFactor:V,repetitions:Z,nextReviewAt:this.addDays(W,H),learningStep:J,isGraduated:!0,isRelearning:$,lapses:q}}addMinutes(j,Q){return new Date(j.getTime()+Q*60*1000)}addDays(j,Q){return new Date(j.getTime()+Q*24*60*60*1000)}}var I=new K;var O={timezone:"UTC",freezesPerMonth:2,maxFreezes:5,gracePeriodHours:4};class h{config;constructor(j={}){this.config={...O,...j}}update(j,Q=new Date){let W=this.getDateString(Q),H={state:{...j},streakMaintained:!1,streakLost:!1,freezeUsed:!1,newStreak:!1,daysMissed:0};if(!j.lastActivityDate)return H.state.currentStreak=1,H.state.longestStreak=Math.max(1,j.longestStreak),H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.newStreak=!0,H.streakMaintained=!0,H;if(j.lastActivityDate===W)return H.state.lastActivityAt=Q,H.streakMaintained=!0,H;let V=this.getDaysBetween(j.lastActivityDate,W);if(V===1)return H.state.currentStreak=j.currentStreak+1,H.state.longestStreak=Math.max(H.state.currentStreak,j.longestStreak),H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.streakMaintained=!0,H;H.daysMissed=V-1;let Z=H.daysMissed;if(Z<=j.freezesRemaining)return H.state.freezesRemaining=j.freezesRemaining-Z,H.state.freezeUsedAt=Q,H.state.currentStreak=j.currentStreak+1,H.state.longestStreak=Math.max(H.state.currentStreak,j.longestStreak),H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.freezeUsed=!0,H.streakMaintained=!0,H;return H.streakLost=!0,H.state.currentStreak=1,H.state.lastActivityAt=Q,H.state.lastActivityDate=W,H.newStreak=!0,H}checkStatus(j,Q=new Date){if(!j.lastActivityDate)return{isActive:!1,willExpireAt:null,canUseFreeze:!1,daysUntilExpiry:0};let W=this.getDateString(Q),H=this.getDaysBetween(j.lastActivityDate,W);if(H===0){let Z=this.addDays(Q,1);return Z.setHours(23,59,59,999),{isActive:!0,willExpireAt:Z,canUseFreeze:j.freezesRemaining>0,daysUntilExpiry:1}}if(H===1){let Z=new Date(Q);return Z.setHours(23+this.config.gracePeriodHours,59,59,999),{isActive:!0,willExpireAt:Z,canUseFreeze:j.freezesRemaining>0,daysUntilExpiry:0}}let V=H-1;return{isActive:V<=j.freezesRemaining,willExpireAt:null,canUseFreeze:V<=j.freezesRemaining,daysUntilExpiry:-V}}useFreeze(j,Q=new Date){if(j.freezesRemaining<=0)return null;return{...j,freezesRemaining:j.freezesRemaining-1,freezeUsedAt:Q}}awardMonthlyFreezes(j){return{...j,freezesRemaining:Math.min(j.freezesRemaining+this.config.freezesPerMonth,this.config.maxFreezes)}}getInitialState(){return{currentStreak:0,longestStreak:0,lastActivityAt:null,lastActivityDate:null,freezesRemaining:this.config.freezesPerMonth,freezeUsedAt:null}}getMilestones(j){let Q=[3,7,14,30,60,90,180,365,500,1000],W=Q.filter((V)=>j>=V),H=Q.find((V)=>j<V)??null;return{achieved:W,next:H}}getDateString(j){let Q=j.getFullYear(),W=String(j.getMonth()+1).padStart(2,"0"),H=String(j.getDate()).padStart(2,"0");return`${Q}-${W}-${H}`}getDaysBetween(j,Q){let W=new Date(j),V=new Date(Q).getTime()-W.getTime();return Math.floor(V/86400000)}addDays(j,Q){return new Date(j.getTime()+Q*24*60*60*1000)}}var x=new h;import{defineTranslation as L}from"@contractspec/lib.contracts-spec/translations";var B=L({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 G}from"@contractspec/lib.contracts-spec/translations";var M=G({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 U=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 m}from"@contractspec/lib.contracts-spec/translations";var z=m({specKey:"learning-journey.messages",catalogs:[B,U,M]}),P=z.create,v=z.getDefault,d=z.resetRegistry;var Y={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 C{config;constructor(j={}){this.config={...Y,...j,baseValues:{...Y.baseValues,...j.baseValues},scoreThresholds:j.scoreThresholds||Y.scoreThresholds,streakTiers:j.streakTiers||Y.streakTiers}}calculate(j){let Q=[],W=j.baseXp??this.config.baseValues[j.activity],H=W;if(Q.push({source:"base",amount:W}),j.score!==void 0){let V=this.getScoreMultiplier(j.score);if(V!==1){let Z=Math.round(W*(V-1));H+=Z,Q.push({source:"score_bonus",amount:Z,multiplier:V})}if(j.score===100){let Z=Math.round(W*(this.config.perfectScoreMultiplier-1));H+=Z,Q.push({source:"perfect_score",amount:Z,multiplier:this.config.perfectScoreMultiplier})}}if(j.attemptNumber===1&&!j.isRetry)H+=this.config.firstAttemptBonus,Q.push({source:"first_attempt",amount:this.config.firstAttemptBonus});if(j.isRetry){let V=Math.round(H*(1-this.config.retryPenalty));H-=V,Q.push({source:"retry_penalty",amount:-V,multiplier:this.config.retryPenalty})}if(j.currentStreak&&j.currentStreak>0){let V=this.getStreakBonus(j.currentStreak);if(V>0)H+=V,Q.push({source:"streak_bonus",amount:V})}if(W>0)H=Math.max(1,H);return{totalXp:Math.round(H),baseXp:W,breakdown:Q}}calculateStreakBonus(j){let Q=this.getStreakBonus(j);return{totalXp:Q,baseXp:Q,breakdown:[{source:"streak_bonus",amount:Q}]}}getXpForLevel(j){if(j<=1)return 0;return Math.round(100*Math.pow(j-1,1.5))}getLevelFromXp(j){let Q=1,W=this.getXpForLevel(Q+1);while(j>=W&&Q<1000)Q++,W=this.getXpForLevel(Q+1);let H=this.getXpForLevel(Q),V=this.getXpForLevel(Q+1);return{level:Q,xpInLevel:j-H,xpForNextLevel:V-H}}getScoreMultiplier(j){for(let Q of this.config.scoreThresholds)if(j>=Q.min)return Q.multiplier;return 1}getStreakBonus(j){for(let Q of this.config.streakTiers)if(j>=Q.days)return Q.bonus;return 0}}var _={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 u(j,Q){let W=P(Q),H=_[j];return H?W.t(H):j}var w=new C;export{w as xpEngine,x as streakEngine,I as srsEngine,u as getXpSourceLabel,C as XPEngine,h as StreakEngine,K as SRSEngine,Y as DEFAULT_XP_CONFIG,O as DEFAULT_STREAK_CONFIG,N as DEFAULT_SRS_CONFIG};
|
package/dist/node/engines/xp.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineTranslation as R}from"@contractspec/lib.contracts-spec/translations";var m=R({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 S}from"@contractspec/lib.contracts-spec/translations";var J=S({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 h}from"@contractspec/lib.contracts-spec/translations";var Y=h({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 G}from"@contractspec/lib.contracts-spec/translations";var $=G({specKey:"learning-journey.messages",catalogs:[m,Y,J]}),q=$.create,T=$.getDefault,E=$.resetRegistry;var Z={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,
|
|
1
|
+
import{defineTranslation as R}from"@contractspec/lib.contracts-spec/translations";var m=R({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 S}from"@contractspec/lib.contracts-spec/translations";var J=S({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 h}from"@contractspec/lib.contracts-spec/translations";var Y=h({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 G}from"@contractspec/lib.contracts-spec/translations";var $=G({specKey:"learning-journey.messages",catalogs:[m,Y,J]}),q=$.create,T=$.getDefault,E=$.resetRegistry;var Z={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 K{config;constructor(j={}){this.config={...Z,...j,baseValues:{...Z.baseValues,...j.baseValues},scoreThresholds:j.scoreThresholds||Z.scoreThresholds,streakTiers:j.streakTiers||Z.streakTiers}}calculate(j){let z=[],V=j.baseXp??this.config.baseValues[j.activity],H=V;if(z.push({source:"base",amount:V}),j.score!==void 0){let Q=this.getScoreMultiplier(j.score);if(Q!==1){let W=Math.round(V*(Q-1));H+=W,z.push({source:"score_bonus",amount:W,multiplier:Q})}if(j.score===100){let W=Math.round(V*(this.config.perfectScoreMultiplier-1));H+=W,z.push({source:"perfect_score",amount:W,multiplier:this.config.perfectScoreMultiplier})}}if(j.attemptNumber===1&&!j.isRetry)H+=this.config.firstAttemptBonus,z.push({source:"first_attempt",amount:this.config.firstAttemptBonus});if(j.isRetry){let Q=Math.round(H*(1-this.config.retryPenalty));H-=Q,z.push({source:"retry_penalty",amount:-Q,multiplier:this.config.retryPenalty})}if(j.currentStreak&&j.currentStreak>0){let Q=this.getStreakBonus(j.currentStreak);if(Q>0)H+=Q,z.push({source:"streak_bonus",amount:Q})}if(V>0)H=Math.max(1,H);return{totalXp:Math.round(H),baseXp:V,breakdown:z}}calculateStreakBonus(j){let z=this.getStreakBonus(j);return{totalXp:z,baseXp:z,breakdown:[{source:"streak_bonus",amount:z}]}}getXpForLevel(j){if(j<=1)return 0;return Math.round(100*Math.pow(j-1,1.5))}getLevelFromXp(j){let z=1,V=this.getXpForLevel(z+1);while(j>=V&&z<1000)z++,V=this.getXpForLevel(z+1);let H=this.getXpForLevel(z),Q=this.getXpForLevel(z+1);return{level:z,xpInLevel:j-H,xpForNextLevel:Q-H}}getScoreMultiplier(j){for(let z of this.config.scoreThresholds)if(j>=z.min)return z.multiplier;return 1}getStreakBonus(j){for(let z of this.config.streakTiers)if(j>=z.days)return z.bonus;return 0}}var A={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 y(j,z){let V=q(z),H=A[j];return H?V.t(H):j}var _=new K;export{_ as xpEngine,y as getXpSourceLabel,K as XPEngine,Z as DEFAULT_XP_CONFIG};
|