@contractspec/example.learning-journey-registry 0.0.0-canary-20260113170453
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/.turbo/turbo-build$colon$bundle.log +50 -0
- package/.turbo/turbo-build.log +51 -0
- package/CHANGELOG.md +612 -0
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/api-types.d.ts +41 -0
- package/dist/api-types.d.ts.map +1 -0
- package/dist/api-types.js +0 -0
- package/dist/api.d.ts +13 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +171 -0
- package/dist/api.js.map +1 -0
- package/dist/docs/index.d.ts +1 -0
- package/dist/docs/index.js +1 -0
- package/dist/docs/learning-journey-registry.docblock.d.ts +1 -0
- package/dist/docs/learning-journey-registry.docblock.js +38 -0
- package/dist/docs/learning-journey-registry.docblock.js.map +1 -0
- package/dist/example.d.ts +7 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +42 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/learning-journey-registry.feature.d.ts +12 -0
- package/dist/learning-journey-registry.feature.d.ts.map +1 -0
- package/dist/learning-journey-registry.feature.js +75 -0
- package/dist/learning-journey-registry.feature.js.map +1 -0
- package/dist/presentations/index.d.ts +10 -0
- package/dist/presentations/index.d.ts.map +1 -0
- package/dist/presentations/index.js +71 -0
- package/dist/presentations/index.js.map +1 -0
- package/dist/progress-store.d.ts +11 -0
- package/dist/progress-store.d.ts.map +1 -0
- package/dist/progress-store.js +31 -0
- package/dist/progress-store.js.map +1 -0
- package/dist/tracks.d.ts +40 -0
- package/dist/tracks.d.ts.map +1 -0
- package/dist/tracks.js +48 -0
- package/dist/tracks.js.map +1 -0
- package/dist/ui/LearningMiniApp.d.ts +24 -0
- package/dist/ui/LearningMiniApp.d.ts.map +1 -0
- package/dist/ui/LearningMiniApp.js +80 -0
- package/dist/ui/LearningMiniApp.js.map +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +3 -0
- package/example.ts +1 -0
- package/package.json +86 -0
- package/src/api-types.ts +43 -0
- package/src/api.test.ts +46 -0
- package/src/api.ts +301 -0
- package/src/docs/index.ts +1 -0
- package/src/docs/learning-journey-registry.docblock.ts +36 -0
- package/src/example.ts +31 -0
- package/src/index.ts +8 -0
- package/src/learning-journey-registry.feature.ts +64 -0
- package/src/presentations/index.ts +65 -0
- package/src/progress-store.ts +39 -0
- package/src/tracks.ts +91 -0
- package/src/ui/LearningMiniApp.tsx +121 -0
- package/src/ui/index.ts +5 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +17 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LearningJourneyTrackSpec } from "@contractspec/module.learning-journey/track-spec";
|
|
2
|
+
|
|
3
|
+
//#region src/api-types.d.ts
|
|
4
|
+
interface LearningEvent {
|
|
5
|
+
name: string;
|
|
6
|
+
version?: number;
|
|
7
|
+
sourceModule?: string;
|
|
8
|
+
payload?: Record<string, unknown>;
|
|
9
|
+
occurredAt?: Date;
|
|
10
|
+
learnerId: string;
|
|
11
|
+
trackId?: string;
|
|
12
|
+
}
|
|
13
|
+
type StepStatus = 'PENDING' | 'COMPLETED';
|
|
14
|
+
interface StepProgress {
|
|
15
|
+
id: string;
|
|
16
|
+
status: StepStatus;
|
|
17
|
+
xpEarned: number;
|
|
18
|
+
completedAt?: Date;
|
|
19
|
+
triggeringEvent?: string;
|
|
20
|
+
eventPayload?: Record<string, unknown>;
|
|
21
|
+
occurrences?: number;
|
|
22
|
+
counterStartedAt?: Date;
|
|
23
|
+
availableAt?: Date;
|
|
24
|
+
dueAt?: Date;
|
|
25
|
+
masteryCount?: number;
|
|
26
|
+
}
|
|
27
|
+
interface TrackProgress {
|
|
28
|
+
learnerId: string;
|
|
29
|
+
trackId: string;
|
|
30
|
+
progress: number;
|
|
31
|
+
isCompleted: boolean;
|
|
32
|
+
xpEarned: number;
|
|
33
|
+
startedAt?: Date;
|
|
34
|
+
completedAt?: Date;
|
|
35
|
+
lastActivityAt?: Date;
|
|
36
|
+
steps: StepProgress[];
|
|
37
|
+
}
|
|
38
|
+
type TrackResolver = (trackId: string) => LearningJourneyTrackSpec | undefined;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { LearningEvent, StepProgress, StepStatus, TrackProgress, TrackResolver };
|
|
41
|
+
//# sourceMappingURL=api-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-types.d.ts","names":[],"sources":["../src/api-types.ts"],"sourcesContent":[],"mappings":";;;UAEiB,aAAA;;EAAA,OAAA,CAAA,EAAA,MAAa;EAUlB,YAAA,CAAU,EAAA,MAAA;EAEL,OAAA,CAAA,EARL,MAQiB,CAAA,MAAA,EAAA,OAAA,CAAA;EAEnB,UAAA,CAAA,EATK,IASL;EAEM,SAAA,EAAA,MAAA;EAEC,OAAA,CAAA,EAAA,MAAA;;AAGD,KAXJ,UAAA,GAWI,SAAA,GAAA,WAAA;AACN,UAVO,YAAA,CAUP;EAAI,EAAA,EAAA,MAAA;EAIG,MAAA,EAZP,UAYoB;EAMhB,QAAA,EAAA,MAAA;EACE,WAAA,CAAA,EAjBA,IAiBA;EACG,eAAA,CAAA,EAAA,MAAA;EACV,YAAA,CAAA,EAjBQ,MAiBR,CAAA,MAAA,EAAA,OAAA,CAAA;EAAY,WAAA,CAAA,EAAA,MAAA;EAGT,gBAAa,CAAA,EAlBJ,IAkBI;gBAjBT;UACN;;;UAIO,aAAA;;;;;;cAMH;gBACE;mBACG;SACV;;KAGG,aAAA,wBAEP"}
|
|
File without changes
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LearningEvent, TrackProgress } from "./api-types.js";
|
|
2
|
+
import { LearningJourneyTrackSpec } from "@contractspec/module.learning-journey/track-spec";
|
|
3
|
+
|
|
4
|
+
//#region src/api.d.ts
|
|
5
|
+
declare const listTracks: (learnerId?: string) => {
|
|
6
|
+
tracks: LearningJourneyTrackSpec[];
|
|
7
|
+
progress: TrackProgress[];
|
|
8
|
+
};
|
|
9
|
+
declare const getProgress: (trackId: string, learnerId: string) => TrackProgress | undefined;
|
|
10
|
+
declare const recordEvent: (event: LearningEvent) => TrackProgress[];
|
|
11
|
+
//#endregion
|
|
12
|
+
export { getProgress, listTracks, recordEvent };
|
|
13
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","names":[],"sources":["../src/api.ts"],"sourcesContent":[],"mappings":";;;;cAyLa;UAWZ;EAXY,QAAA,EAWZ,aAAA,EAAA;AAED,CAAA;AAUa,cAVA,WAUsB,EAAA,CAAA,OAAA,EAAA,MAAa,EAAA,SAAA,EAAA,MAAA,EAAA,GAVc,aAUd,GAAA,SAAA;cAAnC,qBAAsB,kBAAa"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { learningJourneyTracks } from "./tracks.js";
|
|
2
|
+
import { getLearnerTracks, getTrackResolver, initProgress } from "./progress-store.js";
|
|
3
|
+
|
|
4
|
+
//#region src/api.ts
|
|
5
|
+
const getTrack = getTrackResolver(learningJourneyTracks);
|
|
6
|
+
const matchesFilter = (filter, payload) => {
|
|
7
|
+
if (!filter) return true;
|
|
8
|
+
if (!payload) return false;
|
|
9
|
+
return Object.entries(filter).every(([key, value]) => payload[key] === value);
|
|
10
|
+
};
|
|
11
|
+
const matchesBaseEvent = (condition, event) => {
|
|
12
|
+
if (condition.eventName !== event.name) return false;
|
|
13
|
+
if (condition.eventVersion !== void 0 && event.version !== void 0) {
|
|
14
|
+
if (condition.eventVersion !== event.version) return false;
|
|
15
|
+
}
|
|
16
|
+
if (condition.sourceModule && event.sourceModule && condition.sourceModule !== event.sourceModule) return false;
|
|
17
|
+
return matchesFilter(condition.payloadFilter, event.payload);
|
|
18
|
+
};
|
|
19
|
+
const matchesCondition = (condition, event, step, trackStartedAt) => {
|
|
20
|
+
if (condition.kind === "count") {
|
|
21
|
+
if (!matchesBaseEvent(condition, event)) return { matched: false };
|
|
22
|
+
const occurrences = (step.occurrences ?? 0) + 1;
|
|
23
|
+
return {
|
|
24
|
+
matched: (condition.withinHours === void 0 || Boolean(trackStartedAt && event.occurredAt && (event.occurredAt.getTime() - trackStartedAt.getTime()) / (1e3 * 60 * 60) <= condition.withinHours)) && occurrences >= condition.atLeast,
|
|
25
|
+
occurrences
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (condition.kind === "time_window") {
|
|
29
|
+
if (!matchesBaseEvent(condition, event)) return { matched: false };
|
|
30
|
+
if (condition.withinHoursOfStart !== void 0 && trackStartedAt && event.occurredAt) {
|
|
31
|
+
if ((event.occurredAt.getTime() - trackStartedAt.getTime()) / (1e3 * 60 * 60) > condition.withinHoursOfStart) return { matched: false };
|
|
32
|
+
}
|
|
33
|
+
return { matched: true };
|
|
34
|
+
}
|
|
35
|
+
if (condition.kind === "srs_mastery") {
|
|
36
|
+
if (event.name !== condition.eventName) return { matched: false };
|
|
37
|
+
const payload = event.payload;
|
|
38
|
+
if (!matchesFilter(condition.payloadFilter, payload)) return { matched: false };
|
|
39
|
+
const skillKey = condition.skillIdField ?? "skillId";
|
|
40
|
+
const masteryKey = condition.masteryField ?? "mastery";
|
|
41
|
+
const skillId = payload?.[skillKey];
|
|
42
|
+
const masteryValue = payload?.[masteryKey];
|
|
43
|
+
if (skillId === void 0 || masteryValue === void 0) return { matched: false };
|
|
44
|
+
if (typeof masteryValue !== "number") return { matched: false };
|
|
45
|
+
if (masteryValue < condition.minimumMastery) return { matched: false };
|
|
46
|
+
const masteryCount = (step.masteryCount ?? 0) + 1;
|
|
47
|
+
return {
|
|
48
|
+
matched: masteryCount >= (condition.requiredCount ?? 1),
|
|
49
|
+
masteryCount
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { matched: matchesBaseEvent(condition, event) };
|
|
53
|
+
};
|
|
54
|
+
const getAvailability = (availability, startedAt) => {
|
|
55
|
+
if (!availability || !startedAt) return {};
|
|
56
|
+
const baseTime = startedAt.getTime();
|
|
57
|
+
let unlockTime = baseTime;
|
|
58
|
+
if (availability.unlockOnDay !== void 0) unlockTime = baseTime + (availability.unlockOnDay - 1) * 24 * 60 * 60 * 1e3;
|
|
59
|
+
if (availability.unlockAfterHours !== void 0) unlockTime = baseTime + availability.unlockAfterHours * 60 * 60 * 1e3;
|
|
60
|
+
const availableAt = new Date(unlockTime);
|
|
61
|
+
return {
|
|
62
|
+
availableAt,
|
|
63
|
+
dueAt: availability.dueWithinHours !== void 0 ? new Date(availableAt.getTime() + availability.dueWithinHours * 60 * 60 * 1e3) : void 0
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
const computeProgressPercent = (steps) => {
|
|
67
|
+
const total = steps.length || 1;
|
|
68
|
+
const done = steps.filter((s) => s.status === "COMPLETED").length;
|
|
69
|
+
return Math.round(done / total * 100);
|
|
70
|
+
};
|
|
71
|
+
const applyTrackCompletionBonuses = (track, progress) => {
|
|
72
|
+
if (progress.isCompleted) return progress;
|
|
73
|
+
const completedAt = /* @__PURE__ */ new Date();
|
|
74
|
+
const startedAt = progress.startedAt ?? completedAt;
|
|
75
|
+
const hoursElapsed = (completedAt.getTime() - startedAt.getTime()) / (1e3 * 60 * 60);
|
|
76
|
+
let xpEarned = progress.xpEarned;
|
|
77
|
+
const { completionRewards, streakRule } = track;
|
|
78
|
+
if (completionRewards?.xpBonus) xpEarned += completionRewards.xpBonus;
|
|
79
|
+
if (streakRule?.hoursWindow !== void 0 && hoursElapsed <= streakRule.hoursWindow && streakRule.bonusXp) xpEarned += streakRule.bonusXp;
|
|
80
|
+
return {
|
|
81
|
+
...progress,
|
|
82
|
+
xpEarned,
|
|
83
|
+
isCompleted: true,
|
|
84
|
+
completedAt,
|
|
85
|
+
lastActivityAt: completedAt
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const listTracks = (learnerId) => {
|
|
89
|
+
const progressMap = learnerId ? getLearnerTracks(learnerId) : void 0;
|
|
90
|
+
return {
|
|
91
|
+
tracks: learningJourneyTracks,
|
|
92
|
+
progress: learnerId && progressMap ? Array.from(progressMap.values()) : []
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const getProgress = (trackId, learnerId) => {
|
|
96
|
+
const track = getTrack(trackId);
|
|
97
|
+
if (!track) return void 0;
|
|
98
|
+
const map = getLearnerTracks(learnerId);
|
|
99
|
+
const existing = map.get(trackId) ?? initProgress(learnerId, track);
|
|
100
|
+
map.set(trackId, existing);
|
|
101
|
+
return existing;
|
|
102
|
+
};
|
|
103
|
+
const recordEvent = (event) => {
|
|
104
|
+
const targets = event.trackId !== void 0 ? learningJourneyTracks.filter((t) => t.id === event.trackId) : learningJourneyTracks;
|
|
105
|
+
const updated = [];
|
|
106
|
+
const eventTime = event.occurredAt ?? /* @__PURE__ */ new Date();
|
|
107
|
+
for (const track of targets) {
|
|
108
|
+
const map = getLearnerTracks(event.learnerId);
|
|
109
|
+
const current = map.get(track.id) ?? initProgress(event.learnerId, track);
|
|
110
|
+
const startedAt = current.startedAt ?? eventTime;
|
|
111
|
+
let changed = current.startedAt === void 0;
|
|
112
|
+
const steps = current.steps.map((step) => {
|
|
113
|
+
if (step.status === "COMPLETED") return step;
|
|
114
|
+
const spec = track.steps.find((s) => s.id === step.id);
|
|
115
|
+
if (!spec) return step;
|
|
116
|
+
const { availableAt, dueAt } = getAvailability(spec.availability, startedAt);
|
|
117
|
+
if (availableAt && eventTime < availableAt) return {
|
|
118
|
+
...step,
|
|
119
|
+
availableAt,
|
|
120
|
+
dueAt
|
|
121
|
+
};
|
|
122
|
+
if (dueAt && eventTime > dueAt) return {
|
|
123
|
+
...step,
|
|
124
|
+
availableAt,
|
|
125
|
+
dueAt
|
|
126
|
+
};
|
|
127
|
+
const result = matchesCondition(spec.completion, event, step, startedAt);
|
|
128
|
+
if (result.matched) {
|
|
129
|
+
changed = true;
|
|
130
|
+
return {
|
|
131
|
+
...step,
|
|
132
|
+
status: "COMPLETED",
|
|
133
|
+
xpEarned: spec.xpReward ?? 0,
|
|
134
|
+
completedAt: eventTime,
|
|
135
|
+
triggeringEvent: event.name,
|
|
136
|
+
eventPayload: event.payload,
|
|
137
|
+
occurrences: result.occurrences ?? step.occurrences,
|
|
138
|
+
masteryCount: result.masteryCount ?? step.masteryCount,
|
|
139
|
+
availableAt,
|
|
140
|
+
dueAt
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (result.occurrences !== void 0 || result.masteryCount !== void 0) changed = true;
|
|
144
|
+
return {
|
|
145
|
+
...step,
|
|
146
|
+
occurrences: result.occurrences ?? step.occurrences,
|
|
147
|
+
masteryCount: result.masteryCount ?? step.masteryCount,
|
|
148
|
+
availableAt,
|
|
149
|
+
dueAt
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
if (!changed) continue;
|
|
153
|
+
const xpEarned = steps.reduce((sum, s) => sum + s.xpEarned, 0) + (track.totalXp ?? 0);
|
|
154
|
+
let progress = {
|
|
155
|
+
...current,
|
|
156
|
+
steps,
|
|
157
|
+
xpEarned,
|
|
158
|
+
startedAt,
|
|
159
|
+
lastActivityAt: eventTime,
|
|
160
|
+
progress: computeProgressPercent(steps)
|
|
161
|
+
};
|
|
162
|
+
if (steps.every((s) => s.status === "COMPLETED")) progress = applyTrackCompletionBonuses(track, progress);
|
|
163
|
+
map.set(track.id, progress);
|
|
164
|
+
updated.push(progress);
|
|
165
|
+
}
|
|
166
|
+
return updated;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
//#endregion
|
|
170
|
+
export { getProgress, listTracks, recordEvent };
|
|
171
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","names":[],"sources":["../src/api.ts"],"sourcesContent":["import { learningJourneyTracks } from './tracks';\nimport type {\n LearningJourneyTrackSpec,\n StepAvailabilitySpec,\n StepCompletionConditionSpec,\n} from '@contractspec/module.learning-journey/track-spec';\nimport type { LearningEvent, StepProgress, TrackProgress } from './api-types';\nimport {\n getLearnerTracks,\n getTrackResolver,\n initProgress,\n} from './progress-store';\n\nconst getTrack = getTrackResolver(learningJourneyTracks);\n\nconst matchesFilter = (\n filter: Record<string, unknown> | undefined,\n payload: Record<string, unknown> | undefined\n): boolean => {\n if (!filter) return true;\n if (!payload) return false;\n return Object.entries(filter).every(([key, value]) => payload[key] === value);\n};\n\nconst matchesBaseEvent = (\n condition: {\n eventName: string;\n eventVersion?: number;\n sourceModule?: string;\n payloadFilter?: Record<string, unknown>;\n },\n event: LearningEvent\n): boolean => {\n if (condition.eventName !== event.name) return false;\n if (condition.eventVersion !== undefined && event.version !== undefined) {\n if (condition.eventVersion !== event.version) return false;\n }\n if (\n condition.sourceModule &&\n event.sourceModule &&\n condition.sourceModule !== event.sourceModule\n ) {\n return false;\n }\n return matchesFilter(\n condition.payloadFilter,\n event.payload as Record<string, unknown> | undefined\n );\n};\n\nconst matchesCondition = (\n condition: StepCompletionConditionSpec,\n event: LearningEvent,\n step: StepProgress,\n trackStartedAt: Date | undefined\n): {\n matched: boolean;\n occurrences?: number;\n masteryCount?: number;\n} => {\n if (condition.kind === 'count') {\n if (!matchesBaseEvent(condition, event)) return { matched: false };\n const occurrences = (step.occurrences ?? 0) + 1;\n const within =\n condition.withinHours === undefined ||\n Boolean(\n trackStartedAt &&\n event.occurredAt &&\n (event.occurredAt.getTime() - trackStartedAt.getTime()) /\n (1000 * 60 * 60) <=\n condition.withinHours\n );\n return { matched: within && occurrences >= condition.atLeast, occurrences };\n }\n\n if (condition.kind === 'time_window') {\n if (!matchesBaseEvent(condition, event)) return { matched: false };\n if (\n condition.withinHoursOfStart !== undefined &&\n trackStartedAt &&\n event.occurredAt\n ) {\n const hoursSinceStart =\n (event.occurredAt.getTime() - trackStartedAt.getTime()) /\n (1000 * 60 * 60);\n if (hoursSinceStart > condition.withinHoursOfStart) {\n return { matched: false };\n }\n }\n return { matched: true };\n }\n\n if (condition.kind === 'srs_mastery') {\n if (event.name !== condition.eventName) return { matched: false };\n const payload = event.payload as Record<string, unknown> | undefined;\n if (!matchesFilter(condition.payloadFilter, payload)) {\n return { matched: false };\n }\n const skillKey = condition.skillIdField ?? 'skillId';\n const masteryKey = condition.masteryField ?? 'mastery';\n const skillId = payload?.[skillKey];\n const masteryValue = payload?.[masteryKey];\n if (skillId === undefined || masteryValue === undefined) {\n return { matched: false };\n }\n if (typeof masteryValue !== 'number') return { matched: false };\n if (masteryValue < condition.minimumMastery) return { matched: false };\n const masteryCount = (step.masteryCount ?? 0) + 1;\n const required = condition.requiredCount ?? 1;\n return { matched: masteryCount >= required, masteryCount };\n }\n\n return { matched: matchesBaseEvent(condition, event) };\n};\n\nconst getAvailability = (\n availability: StepAvailabilitySpec | undefined,\n startedAt: Date | undefined\n): { availableAt?: Date; dueAt?: Date } => {\n if (!availability || !startedAt) return {};\n\n const baseTime = startedAt.getTime();\n let unlockTime = baseTime;\n\n if (availability.unlockOnDay !== undefined) {\n unlockTime =\n baseTime + (availability.unlockOnDay - 1) * 24 * 60 * 60 * 1000;\n }\n\n if (availability.unlockAfterHours !== undefined) {\n unlockTime = baseTime + availability.unlockAfterHours * 60 * 60 * 1000;\n }\n\n const availableAt = new Date(unlockTime);\n const dueAt =\n availability.dueWithinHours !== undefined\n ? new Date(\n availableAt.getTime() + availability.dueWithinHours * 60 * 60 * 1000\n )\n : undefined;\n\n return { availableAt, dueAt };\n};\n\nconst computeProgressPercent = (steps: StepProgress[]): number => {\n const total = steps.length || 1;\n const done = steps.filter((s) => s.status === 'COMPLETED').length;\n return Math.round((done / total) * 100);\n};\n\nconst applyTrackCompletionBonuses = (\n track: LearningJourneyTrackSpec,\n progress: TrackProgress\n) => {\n if (progress.isCompleted) return progress;\n\n const completedAt = new Date();\n const startedAt = progress.startedAt ?? completedAt;\n const hoursElapsed =\n (completedAt.getTime() - startedAt.getTime()) / (1000 * 60 * 60);\n\n let xpEarned = progress.xpEarned;\n const { completionRewards, streakRule } = track;\n\n if (completionRewards?.xpBonus) {\n xpEarned += completionRewards.xpBonus;\n }\n\n if (\n streakRule?.hoursWindow !== undefined &&\n hoursElapsed <= streakRule.hoursWindow &&\n streakRule.bonusXp\n ) {\n xpEarned += streakRule.bonusXp;\n }\n\n return {\n ...progress,\n xpEarned,\n isCompleted: true,\n completedAt,\n lastActivityAt: completedAt,\n };\n};\n\nexport const listTracks = (learnerId?: string) => {\n const progressMap = learnerId ? getLearnerTracks(learnerId) : undefined;\n const progress =\n learnerId && progressMap\n ? Array.from(progressMap.values())\n : ([] as TrackProgress[]);\n\n return {\n tracks: learningJourneyTracks,\n progress,\n };\n};\n\nexport const getProgress = (trackId: string, learnerId: string) => {\n const track = getTrack(trackId);\n if (!track) return undefined;\n\n const map = getLearnerTracks(learnerId);\n const existing = map.get(trackId) ?? initProgress(learnerId, track);\n map.set(trackId, existing);\n return existing;\n};\n\nexport const recordEvent = (event: LearningEvent) => {\n const targets =\n event.trackId !== undefined\n ? learningJourneyTracks.filter((t) => t.id === event.trackId)\n : learningJourneyTracks;\n\n const updated: TrackProgress[] = [];\n const eventTime = event.occurredAt ?? new Date();\n\n for (const track of targets) {\n const map = getLearnerTracks(event.learnerId);\n const current = map.get(track.id) ?? initProgress(event.learnerId, track);\n const startedAt = current.startedAt ?? eventTime;\n\n let changed = current.startedAt === undefined;\n const steps: StepProgress[] = current.steps.map((step) => {\n if (step.status === 'COMPLETED') return step;\n\n const spec = track.steps.find((s) => s.id === step.id);\n if (!spec) return step;\n\n const { availableAt, dueAt } = getAvailability(\n spec.availability,\n startedAt\n );\n if (availableAt && eventTime < availableAt) {\n return { ...step, availableAt, dueAt };\n }\n if (dueAt && eventTime > dueAt) {\n // keep pending but note deadlines\n return { ...step, availableAt, dueAt };\n }\n\n const result = matchesCondition(spec.completion, event, step, startedAt);\n\n if (result.matched) {\n changed = true;\n return {\n ...step,\n status: 'COMPLETED',\n xpEarned: spec.xpReward ?? 0,\n completedAt: eventTime,\n triggeringEvent: event.name,\n eventPayload: event.payload,\n occurrences: result.occurrences ?? step.occurrences,\n masteryCount: result.masteryCount ?? step.masteryCount,\n availableAt,\n dueAt,\n };\n }\n\n if (\n result.occurrences !== undefined ||\n result.masteryCount !== undefined\n ) {\n changed = true;\n }\n\n return {\n ...step,\n occurrences: result.occurrences ?? step.occurrences,\n masteryCount: result.masteryCount ?? step.masteryCount,\n availableAt,\n dueAt,\n };\n });\n\n if (!changed) {\n continue;\n }\n\n const xpEarned =\n steps.reduce((sum, s) => sum + s.xpEarned, 0) + (track.totalXp ?? 0);\n let progress: TrackProgress = {\n ...current,\n steps,\n xpEarned,\n startedAt,\n lastActivityAt: eventTime,\n progress: computeProgressPercent(steps),\n };\n\n const allDone = steps.every((s) => s.status === 'COMPLETED');\n if (allDone) {\n progress = applyTrackCompletionBonuses(track, progress);\n }\n\n map.set(track.id, progress);\n updated.push(progress);\n }\n\n return updated;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./learning-journey-registry.docblock.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { registerDocBlocks } from "@contractspec/lib.contracts/docs";
|
|
2
|
+
|
|
3
|
+
//#region src/docs/learning-journey-registry.docblock.ts
|
|
4
|
+
registerDocBlocks([{
|
|
5
|
+
id: "docs.learning-journey.registry",
|
|
6
|
+
title: "Learning Journey — Example Track Registry",
|
|
7
|
+
summary: "Aggregates learning journey example tracks (Studio onboarding, Platform tour, CRM first win, Drills, Ambient Coach, Quest challenges).",
|
|
8
|
+
kind: "usage",
|
|
9
|
+
visibility: "public",
|
|
10
|
+
route: "/docs/learning-journey/registry",
|
|
11
|
+
tags: [
|
|
12
|
+
"learning",
|
|
13
|
+
"registry",
|
|
14
|
+
"onboarding"
|
|
15
|
+
],
|
|
16
|
+
body: `## Tracks
|
|
17
|
+
- \`studio_getting_started\` (Studio onboarding)
|
|
18
|
+
- \`platform_primitives_tour\` (Platform primitives)
|
|
19
|
+
- \`crm_first_win\` (CRM pipeline onboarding)
|
|
20
|
+
- \`drills_language_basics\` (Drills & SRS)
|
|
21
|
+
- \`money_ambient_coach\`, \`coliving_ambient_coach\` (Ambient tips)
|
|
22
|
+
- \`money_reset_7day\` (Quest/challenge)
|
|
23
|
+
|
|
24
|
+
## Exports
|
|
25
|
+
- \`learningJourneyTracks\` — raw specs
|
|
26
|
+
- \`onboardingTrackCatalog\` — DTOs aligned with onboarding API
|
|
27
|
+
- \`mapTrackSpecToDto\` — helper to map individual tracks
|
|
28
|
+
|
|
29
|
+
## Wiring
|
|
30
|
+
- Use with onboarding API contracts:
|
|
31
|
+
- \`learning.onboarding.listTracks\`
|
|
32
|
+
- \`learning.onboarding.getProgress\`
|
|
33
|
+
- \`learning.onboarding.recordEvent\`
|
|
34
|
+
- Intended for registry/adapters in Studio UI or services that surface tracks.`
|
|
35
|
+
}]);
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//# sourceMappingURL=learning-journey-registry.docblock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-journey-registry.docblock.js","names":[],"sources":["../../src/docs/learning-journey-registry.docblock.ts"],"sourcesContent":["import type { DocBlock } from '@contractspec/lib.contracts/docs';\nimport { registerDocBlocks } from '@contractspec/lib.contracts/docs';\n\nconst registryDocBlocks: DocBlock[] = [\n {\n id: 'docs.learning-journey.registry',\n title: 'Learning Journey — Example Track Registry',\n summary:\n 'Aggregates learning journey example tracks (Studio onboarding, Platform tour, CRM first win, Drills, Ambient Coach, Quest challenges).',\n kind: 'usage',\n visibility: 'public',\n route: '/docs/learning-journey/registry',\n tags: ['learning', 'registry', 'onboarding'],\n body: `## Tracks\n- \\`studio_getting_started\\` (Studio onboarding)\n- \\`platform_primitives_tour\\` (Platform primitives)\n- \\`crm_first_win\\` (CRM pipeline onboarding)\n- \\`drills_language_basics\\` (Drills & SRS)\n- \\`money_ambient_coach\\`, \\`coliving_ambient_coach\\` (Ambient tips)\n- \\`money_reset_7day\\` (Quest/challenge)\n\n## Exports\n- \\`learningJourneyTracks\\` — raw specs\n- \\`onboardingTrackCatalog\\` — DTOs aligned with onboarding API\n- \\`mapTrackSpecToDto\\` — helper to map individual tracks\n\n## Wiring\n- Use with onboarding API contracts:\n - \\`learning.onboarding.listTracks\\`\n - \\`learning.onboarding.getProgress\\`\n - \\`learning.onboarding.recordEvent\\`\n- Intended for registry/adapters in Studio UI or services that surface tracks.`,\n },\n];\n\nregisterDocBlocks(registryDocBlocks);\n"],"mappings":";;;AAmCA,kBAhCsC,CACpC;CACE,IAAI;CACJ,OAAO;CACP,SACE;CACF,MAAM;CACN,YAAY;CACZ,OAAO;CACP,MAAM;EAAC;EAAY;EAAY;EAAa;CAC5C,MAAM;;;;;;;;;;;;;;;;;;;CAmBP,CACF,CAEmC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example.d.ts","names":[],"sources":["../src/example.ts"],"sourcesContent":[],"mappings":";;;cAEM,SA0BJ,4BAAA,CA1BW"}
|
package/dist/example.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defineExample } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/example.ts
|
|
4
|
+
const example = defineExample({
|
|
5
|
+
meta: {
|
|
6
|
+
key: "learning-journey-registry",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
title: "Learning Journey Registry",
|
|
9
|
+
description: "Registry of learning journey tracks + presentations + UI mini-app bindings.",
|
|
10
|
+
kind: "library",
|
|
11
|
+
visibility: "public",
|
|
12
|
+
stability: "experimental",
|
|
13
|
+
owners: ["@platform.core"],
|
|
14
|
+
tags: [
|
|
15
|
+
"learning",
|
|
16
|
+
"journey",
|
|
17
|
+
"registry"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
docs: { rootDocId: "docs.examples.learning-journey-registry" },
|
|
21
|
+
entrypoints: {
|
|
22
|
+
packageName: "@contractspec/example.learning-journey-registry",
|
|
23
|
+
docs: "./docs"
|
|
24
|
+
},
|
|
25
|
+
surfaces: {
|
|
26
|
+
templates: true,
|
|
27
|
+
sandbox: {
|
|
28
|
+
enabled: true,
|
|
29
|
+
modes: ["markdown", "specs"]
|
|
30
|
+
},
|
|
31
|
+
studio: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
installable: true
|
|
34
|
+
},
|
|
35
|
+
mcp: { enabled: true }
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
var example_default = example;
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { example_default as default };
|
|
42
|
+
//# sourceMappingURL=example.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example.js","names":[],"sources":["../src/example.ts"],"sourcesContent":["import { defineExample } from '@contractspec/lib.contracts';\n\nconst example = defineExample({\n meta: {\n key: 'learning-journey-registry',\n version: '1.0.0',\n title: 'Learning Journey Registry',\n description:\n 'Registry of learning journey tracks + presentations + UI mini-app bindings.',\n kind: 'library',\n visibility: 'public',\n stability: 'experimental',\n owners: ['@platform.core'],\n tags: ['learning', 'journey', 'registry'],\n },\n docs: {\n rootDocId: 'docs.examples.learning-journey-registry',\n },\n entrypoints: {\n packageName: '@contractspec/example.learning-journey-registry',\n docs: './docs',\n },\n surfaces: {\n templates: true,\n sandbox: { enabled: true, modes: ['markdown', 'specs'] },\n studio: { enabled: true, installable: true },\n mcp: { enabled: true },\n },\n});\n\nexport default example;\n"],"mappings":";;;AAEA,MAAM,UAAU,cAAc;CAC5B,MAAM;EACJ,KAAK;EACL,SAAS;EACT,OAAO;EACP,aACE;EACF,MAAM;EACN,YAAY;EACZ,WAAW;EACX,QAAQ,CAAC,iBAAiB;EAC1B,MAAM;GAAC;GAAY;GAAW;GAAW;EAC1C;CACD,MAAM,EACJ,WAAW,2CACZ;CACD,aAAa;EACX,aAAa;EACb,MAAM;EACP;CACD,UAAU;EACR,WAAW;EACX,SAAS;GAAE,SAAS;GAAM,OAAO,CAAC,YAAY,QAAQ;GAAE;EACxD,QAAQ;GAAE,SAAS;GAAM,aAAa;GAAM;EAC5C,KAAK,EAAE,SAAS,MAAM;EACvB;CACF,CAAC;AAEF,sBAAe"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getProgress, listTracks, recordEvent } from "./api.js";
|
|
2
|
+
import example from "./example.js";
|
|
3
|
+
import { OnboardingStepDto, OnboardingTrackDto, crmLearningTracks, learningJourneyTracks, mapStep, mapTrackSpecToDto, onboardingTrackCatalog, platformLearningTracks, studioLearningTracks } from "./tracks.js";
|
|
4
|
+
import { LearningTrackDetailPresentation, LearningTrackListPresentation, LearningTrackProgressWidgetPresentation, learningJourneyPresentations } from "./presentations/index.js";
|
|
5
|
+
import { LearningJourneyRegistryFeature } from "./learning-journey-registry.feature.js";
|
|
6
|
+
import { LearningMiniApp, getLearningTemplateIds, isLearningTemplate } from "./ui/LearningMiniApp.js";
|
|
7
|
+
import "./ui/index.js";
|
|
8
|
+
export { LearningJourneyRegistryFeature, LearningMiniApp, LearningTrackDetailPresentation, LearningTrackListPresentation, LearningTrackProgressWidgetPresentation, OnboardingStepDto, OnboardingTrackDto, crmLearningTracks, example, getLearningTemplateIds, getProgress, isLearningTemplate, learningJourneyPresentations, learningJourneyTracks, listTracks, mapStep, mapTrackSpecToDto, onboardingTrackCatalog, platformLearningTracks, recordEvent, studioLearningTracks };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { crmLearningTracks, learningJourneyTracks, mapStep, mapTrackSpecToDto, onboardingTrackCatalog, platformLearningTracks, studioLearningTracks } from "./tracks.js";
|
|
2
|
+
import { getProgress, listTracks, recordEvent } from "./api.js";
|
|
3
|
+
import example_default from "./example.js";
|
|
4
|
+
import "./docs/index.js";
|
|
5
|
+
import { LearningTrackDetailPresentation, LearningTrackListPresentation, LearningTrackProgressWidgetPresentation, learningJourneyPresentations } from "./presentations/index.js";
|
|
6
|
+
import { LearningJourneyRegistryFeature } from "./learning-journey-registry.feature.js";
|
|
7
|
+
import { LearningMiniApp, getLearningTemplateIds, isLearningTemplate } from "./ui/LearningMiniApp.js";
|
|
8
|
+
import "./ui/index.js";
|
|
9
|
+
|
|
10
|
+
export { LearningJourneyRegistryFeature, LearningMiniApp, LearningTrackDetailPresentation, LearningTrackListPresentation, LearningTrackProgressWidgetPresentation, crmLearningTracks, example_default as example, getLearningTemplateIds, getProgress, isLearningTemplate, learningJourneyPresentations, learningJourneyTracks, listTracks, mapStep, mapTrackSpecToDto, onboardingTrackCatalog, platformLearningTracks, recordEvent, studioLearningTracks };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as _contractspec_lib_contracts0 from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/learning-journey-registry.feature.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Learning Journey Registry feature module that bundles
|
|
7
|
+
* the shared presentations for learning journey tracks.
|
|
8
|
+
*/
|
|
9
|
+
declare const LearningJourneyRegistryFeature: _contractspec_lib_contracts0.FeatureModuleSpec;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { LearningJourneyRegistryFeature };
|
|
12
|
+
//# sourceMappingURL=learning-journey-registry.feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-journey-registry.feature.d.ts","names":[],"sources":["../src/learning-journey-registry.feature.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;cAAa,gCAoDX,4BAAA,CApDyC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { defineFeature } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/learning-journey-registry.feature.ts
|
|
4
|
+
/**
|
|
5
|
+
* Learning Journey Registry Feature Module Specification
|
|
6
|
+
*
|
|
7
|
+
* Defines the feature module for the learning journey registry.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Learning Journey Registry feature module that bundles
|
|
11
|
+
* the shared presentations for learning journey tracks.
|
|
12
|
+
*/
|
|
13
|
+
const LearningJourneyRegistryFeature = defineFeature({
|
|
14
|
+
meta: {
|
|
15
|
+
key: "learning-journey-registry",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
title: "Learning Journey Registry",
|
|
18
|
+
description: "Shared registry and presentations for learning journey tracks",
|
|
19
|
+
domain: "learning-journey",
|
|
20
|
+
owners: ["@learning-team"],
|
|
21
|
+
tags: [
|
|
22
|
+
"learning",
|
|
23
|
+
"journey",
|
|
24
|
+
"onboarding",
|
|
25
|
+
"registry"
|
|
26
|
+
],
|
|
27
|
+
stability: "experimental"
|
|
28
|
+
},
|
|
29
|
+
operations: [],
|
|
30
|
+
events: [],
|
|
31
|
+
presentations: [
|
|
32
|
+
{
|
|
33
|
+
key: "learning.journey.track_list",
|
|
34
|
+
version: "1.0.0"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: "learning.journey.track_detail",
|
|
38
|
+
version: "1.0.0"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "learning.journey.progress_widget",
|
|
42
|
+
version: "1.0.0"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
opToPresentation: [],
|
|
46
|
+
presentationsTargets: [
|
|
47
|
+
{
|
|
48
|
+
key: "learning.journey.track_list",
|
|
49
|
+
version: "1.0.0",
|
|
50
|
+
targets: ["react", "markdown"]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: "learning.journey.track_detail",
|
|
54
|
+
version: "1.0.0",
|
|
55
|
+
targets: [
|
|
56
|
+
"react",
|
|
57
|
+
"markdown",
|
|
58
|
+
"application/json"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: "learning.journey.progress_widget",
|
|
63
|
+
version: "1.0.0",
|
|
64
|
+
targets: ["react"]
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
capabilities: { requires: [{
|
|
68
|
+
key: "identity",
|
|
69
|
+
version: "1.0.0"
|
|
70
|
+
}] }
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { LearningJourneyRegistryFeature };
|
|
75
|
+
//# sourceMappingURL=learning-journey-registry.feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-journey-registry.feature.js","names":[],"sources":["../src/learning-journey-registry.feature.ts"],"sourcesContent":["/**\n * Learning Journey Registry Feature Module Specification\n *\n * Defines the feature module for the learning journey registry.\n */\nimport { defineFeature } from '@contractspec/lib.contracts';\n\n/**\n * Learning Journey Registry feature module that bundles\n * the shared presentations for learning journey tracks.\n */\nexport const LearningJourneyRegistryFeature = defineFeature({\n meta: {\n key: 'learning-journey-registry',\n version: '1.0.0',\n title: 'Learning Journey Registry',\n description:\n 'Shared registry and presentations for learning journey tracks',\n domain: 'learning-journey',\n owners: ['@learning-team'],\n tags: ['learning', 'journey', 'onboarding', 'registry'],\n stability: 'experimental',\n },\n\n // No operations in the registry - it's presentation-only\n operations: [],\n\n // Events emitted by this feature\n events: [],\n\n // Presentations associated with this feature\n presentations: [\n { key: 'learning.journey.track_list', version: '1.0.0' },\n { key: 'learning.journey.track_detail', version: '1.0.0' },\n { key: 'learning.journey.progress_widget', version: '1.0.0' },\n ],\n\n // No op to presentation links for registry\n opToPresentation: [],\n\n // Target requirements for multi-surface rendering\n presentationsTargets: [\n {\n key: 'learning.journey.track_list',\n version: '1.0.0',\n targets: ['react', 'markdown'],\n },\n {\n key: 'learning.journey.track_detail',\n version: '1.0.0',\n targets: ['react', 'markdown', 'application/json'],\n },\n {\n key: 'learning.journey.progress_widget',\n version: '1.0.0',\n targets: ['react'],\n },\n ],\n\n // Capability requirements\n capabilities: {\n requires: [{ key: 'identity', version: '1.0.0' }],\n },\n});\n"],"mappings":";;;;;;;;;;;;AAWA,MAAa,iCAAiC,cAAc;CAC1D,MAAM;EACJ,KAAK;EACL,SAAS;EACT,OAAO;EACP,aACE;EACF,QAAQ;EACR,QAAQ,CAAC,iBAAiB;EAC1B,MAAM;GAAC;GAAY;GAAW;GAAc;GAAW;EACvD,WAAW;EACZ;CAGD,YAAY,EAAE;CAGd,QAAQ,EAAE;CAGV,eAAe;EACb;GAAE,KAAK;GAA+B,SAAS;GAAS;EACxD;GAAE,KAAK;GAAiC,SAAS;GAAS;EAC1D;GAAE,KAAK;GAAoC,SAAS;GAAS;EAC9D;CAGD,kBAAkB,EAAE;CAGpB,sBAAsB;EACpB;GACE,KAAK;GACL,SAAS;GACT,SAAS,CAAC,SAAS,WAAW;GAC/B;EACD;GACE,KAAK;GACL,SAAS;GACT,SAAS;IAAC;IAAS;IAAY;IAAmB;GACnD;EACD;GACE,KAAK;GACL,SAAS;GACT,SAAS,CAAC,QAAQ;GACnB;EACF;CAGD,cAAc,EACZ,UAAU,CAAC;EAAE,KAAK;EAAY,SAAS;EAAS,CAAC,EAClD;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as _contractspec_lib_contracts0 from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/presentations/index.d.ts
|
|
4
|
+
declare const LearningTrackListPresentation: _contractspec_lib_contracts0.PresentationSpec;
|
|
5
|
+
declare const LearningTrackDetailPresentation: _contractspec_lib_contracts0.PresentationSpec;
|
|
6
|
+
declare const LearningTrackProgressWidgetPresentation: _contractspec_lib_contracts0.PresentationSpec;
|
|
7
|
+
declare const learningJourneyPresentations: _contractspec_lib_contracts0.PresentationSpec[];
|
|
8
|
+
//#endregion
|
|
9
|
+
export { LearningTrackDetailPresentation, LearningTrackListPresentation, LearningTrackProgressWidgetPresentation, learningJourneyPresentations };
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/presentations/index.ts"],"sourcesContent":[],"mappings":";;;cAea,+BAaX,4BAAA,CAbwC;cAe7B,iCAaX,4BAAA,CAb0C;cAe/B,yCAaX,4BAAA,CAbkD;AA9BvC,cA6CA,4BAhCX,EAoCD,4BAAA,CAJwC,gBAAA,EAhCvC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { StabilityEnum, definePresentation } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/presentations/index.ts
|
|
4
|
+
const baseMeta = {
|
|
5
|
+
domain: "learning-journey",
|
|
6
|
+
title: "Learning Journey",
|
|
7
|
+
owners: ["@learning-team"],
|
|
8
|
+
tags: [
|
|
9
|
+
"learning",
|
|
10
|
+
"journey",
|
|
11
|
+
"onboarding"
|
|
12
|
+
],
|
|
13
|
+
stability: StabilityEnum.Experimental,
|
|
14
|
+
goal: "Progress through learning tracks",
|
|
15
|
+
context: "Learning journey section"
|
|
16
|
+
};
|
|
17
|
+
const LearningTrackListPresentation = definePresentation({
|
|
18
|
+
meta: {
|
|
19
|
+
key: "learning.journey.track_list",
|
|
20
|
+
version: "1.0.0",
|
|
21
|
+
description: "List of learning journeys available to the learner.",
|
|
22
|
+
...baseMeta
|
|
23
|
+
},
|
|
24
|
+
source: {
|
|
25
|
+
type: "component",
|
|
26
|
+
framework: "react",
|
|
27
|
+
componentKey: "LearningTrackList"
|
|
28
|
+
},
|
|
29
|
+
targets: ["react", "markdown"]
|
|
30
|
+
});
|
|
31
|
+
const LearningTrackDetailPresentation = definePresentation({
|
|
32
|
+
meta: {
|
|
33
|
+
key: "learning.journey.track_detail",
|
|
34
|
+
version: "1.0.0",
|
|
35
|
+
description: "Track detail with steps and progress state.",
|
|
36
|
+
...baseMeta
|
|
37
|
+
},
|
|
38
|
+
source: {
|
|
39
|
+
type: "component",
|
|
40
|
+
framework: "react",
|
|
41
|
+
componentKey: "LearningTrackDetail"
|
|
42
|
+
},
|
|
43
|
+
targets: [
|
|
44
|
+
"react",
|
|
45
|
+
"markdown",
|
|
46
|
+
"application/json"
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
const LearningTrackProgressWidgetPresentation = definePresentation({
|
|
50
|
+
meta: {
|
|
51
|
+
key: "learning.journey.progress_widget",
|
|
52
|
+
version: "1.0.0",
|
|
53
|
+
description: "Compact widget showing progress for active track.",
|
|
54
|
+
...baseMeta
|
|
55
|
+
},
|
|
56
|
+
source: {
|
|
57
|
+
type: "component",
|
|
58
|
+
framework: "react",
|
|
59
|
+
componentKey: "LearningTrackProgressWidget"
|
|
60
|
+
},
|
|
61
|
+
targets: ["react"]
|
|
62
|
+
});
|
|
63
|
+
const learningJourneyPresentations = [
|
|
64
|
+
LearningTrackListPresentation,
|
|
65
|
+
LearningTrackDetailPresentation,
|
|
66
|
+
LearningTrackProgressWidgetPresentation
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { LearningTrackDetailPresentation, LearningTrackListPresentation, LearningTrackProgressWidgetPresentation, learningJourneyPresentations };
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/presentations/index.ts"],"sourcesContent":["import { definePresentation, StabilityEnum, type PresentationSpecMeta } from '@contractspec/lib.contracts';\n\nconst baseMeta: Pick<\n PresentationSpecMeta,\n 'domain' | 'owners' | 'tags' | 'title' | 'stability' | 'goal' | 'context'\n> = {\n domain: 'learning-journey',\n title: 'Learning Journey',\n owners: ['@learning-team'] as string[],\n tags: ['learning', 'journey', 'onboarding'] as string[],\n stability: StabilityEnum.Experimental,\n goal: 'Progress through learning tracks',\n context: 'Learning journey section',\n};\n\nexport const LearningTrackListPresentation = definePresentation({\n meta: {\n key: 'learning.journey.track_list',\n version: '1.0.0',\n description: 'List of learning journeys available to the learner.',\n ...baseMeta,\n },\n source: {\n type: 'component',\n framework: 'react',\n componentKey: 'LearningTrackList',\n },\n targets: ['react', 'markdown'],\n});\n\nexport const LearningTrackDetailPresentation = definePresentation({\n meta: {\n key: 'learning.journey.track_detail',\n version: '1.0.0',\n description: 'Track detail with steps and progress state.',\n ...baseMeta,\n },\n source: {\n type: 'component',\n framework: 'react',\n componentKey: 'LearningTrackDetail',\n },\n targets: ['react', 'markdown', 'application/json'],\n});\n\nexport const LearningTrackProgressWidgetPresentation = definePresentation({\n meta: {\n key: 'learning.journey.progress_widget',\n version: '1.0.0',\n description: 'Compact widget showing progress for active track.',\n ...baseMeta,\n },\n source: {\n type: 'component',\n framework: 'react',\n componentKey: 'LearningTrackProgressWidget',\n },\n targets: ['react'],\n});\n\nexport const learningJourneyPresentations = [\n LearningTrackListPresentation,\n LearningTrackDetailPresentation,\n LearningTrackProgressWidgetPresentation,\n];\n"],"mappings":";;;AAEA,MAAM,WAGF;CACF,QAAQ;CACR,OAAO;CACP,QAAQ,CAAC,iBAAiB;CAC1B,MAAM;EAAC;EAAY;EAAW;EAAa;CAC3C,WAAW,cAAc;CACzB,MAAM;CACN,SAAS;CACV;AAED,MAAa,gCAAgC,mBAAmB;CAC9D,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,GAAG;EACJ;CACD,QAAQ;EACN,MAAM;EACN,WAAW;EACX,cAAc;EACf;CACD,SAAS,CAAC,SAAS,WAAW;CAC/B,CAAC;AAEF,MAAa,kCAAkC,mBAAmB;CAChE,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,GAAG;EACJ;CACD,QAAQ;EACN,MAAM;EACN,WAAW;EACX,cAAc;EACf;CACD,SAAS;EAAC;EAAS;EAAY;EAAmB;CACnD,CAAC;AAEF,MAAa,0CAA0C,mBAAmB;CACxE,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,GAAG;EACJ;CACD,QAAQ;EACN,MAAM;EACN,WAAW;EACX,cAAc;EACf;CACD,SAAS,CAAC,QAAQ;CACnB,CAAC;AAEF,MAAa,+BAA+B;CAC1C;CACA;CACA;CACD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TrackProgress } from "./api-types.js";
|
|
2
|
+
import { LearningJourneyTrackSpec } from "@contractspec/module.learning-journey/track-spec";
|
|
3
|
+
|
|
4
|
+
//#region src/progress-store.d.ts
|
|
5
|
+
declare const progressStore: Map<string, Map<string, TrackProgress>>;
|
|
6
|
+
declare const getTrackResolver: (tracks: LearningJourneyTrackSpec[]) => (trackId: string) => LearningJourneyTrackSpec | undefined;
|
|
7
|
+
declare const getLearnerTracks: (learnerId: string) => Map<string, TrackProgress>;
|
|
8
|
+
declare const initProgress: (learnerId: string, track: LearningJourneyTrackSpec) => TrackProgress;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getLearnerTracks, getTrackResolver, initProgress, progressStore };
|
|
11
|
+
//# sourceMappingURL=progress-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-store.d.ts","names":[],"sources":["../src/progress-store.ts"],"sourcesContent":[],"mappings":";;;;cAIa,eAAa,YAAA,YAAA;cAEb,2BACF,oDACU;AAJR,cAOA,gBAP6D,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GAOxB,GAPwB,CAAA,MAAA,EAOxB,aAPwB,CAAA;AAAhD,cAeb,YAfa,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,KAAA,EAiBjB,wBAjBiB,EAAA,GAkBvB,aAlBuB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/progress-store.ts
|
|
2
|
+
const progressStore = /* @__PURE__ */ new Map();
|
|
3
|
+
const getTrackResolver = (tracks) => (trackId) => tracks.find((t) => t.id === trackId);
|
|
4
|
+
const getLearnerTracks = (learnerId) => {
|
|
5
|
+
const existing = progressStore.get(learnerId);
|
|
6
|
+
if (existing) return existing;
|
|
7
|
+
const map = /* @__PURE__ */ new Map();
|
|
8
|
+
progressStore.set(learnerId, map);
|
|
9
|
+
return map;
|
|
10
|
+
};
|
|
11
|
+
const initProgress = (learnerId, track) => ({
|
|
12
|
+
learnerId,
|
|
13
|
+
trackId: track.id,
|
|
14
|
+
progress: 0,
|
|
15
|
+
isCompleted: false,
|
|
16
|
+
xpEarned: 0,
|
|
17
|
+
steps: track.steps.map((step) => ({
|
|
18
|
+
id: step.id,
|
|
19
|
+
status: "PENDING",
|
|
20
|
+
xpEarned: 0,
|
|
21
|
+
occurrences: 0,
|
|
22
|
+
masteryCount: 0
|
|
23
|
+
})),
|
|
24
|
+
startedAt: void 0,
|
|
25
|
+
completedAt: void 0,
|
|
26
|
+
lastActivityAt: void 0
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { getLearnerTracks, getTrackResolver, initProgress, progressStore };
|
|
31
|
+
//# sourceMappingURL=progress-store.js.map
|