@contractspec/example.learning-journey-registry 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +50 -0
  2. package/.turbo/turbo-build.log +51 -0
  3. package/CHANGELOG.md +348 -0
  4. package/LICENSE +21 -0
  5. package/README.md +25 -0
  6. package/dist/api-types.d.ts +41 -0
  7. package/dist/api-types.d.ts.map +1 -0
  8. package/dist/api-types.js +0 -0
  9. package/dist/api.d.ts +13 -0
  10. package/dist/api.d.ts.map +1 -0
  11. package/dist/api.js +171 -0
  12. package/dist/api.js.map +1 -0
  13. package/dist/docs/index.d.ts +1 -0
  14. package/dist/docs/index.js +1 -0
  15. package/dist/docs/learning-journey-registry.docblock.d.ts +1 -0
  16. package/dist/docs/learning-journey-registry.docblock.js +38 -0
  17. package/dist/docs/learning-journey-registry.docblock.js.map +1 -0
  18. package/dist/example.d.ts +33 -0
  19. package/dist/example.d.ts.map +1 -0
  20. package/dist/example.js +35 -0
  21. package/dist/example.js.map +1 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +10 -0
  24. package/dist/learning-journey-registry.feature.d.ts +12 -0
  25. package/dist/learning-journey-registry.feature.d.ts.map +1 -0
  26. package/dist/learning-journey-registry.feature.js +68 -0
  27. package/dist/learning-journey-registry.feature.js.map +1 -0
  28. package/dist/presentations/index.d.ts +10 -0
  29. package/dist/presentations/index.d.ts.map +1 -0
  30. package/dist/presentations/index.js +71 -0
  31. package/dist/presentations/index.js.map +1 -0
  32. package/dist/progress-store.d.ts +11 -0
  33. package/dist/progress-store.d.ts.map +1 -0
  34. package/dist/progress-store.js +31 -0
  35. package/dist/progress-store.js.map +1 -0
  36. package/dist/tracks.d.ts +40 -0
  37. package/dist/tracks.d.ts.map +1 -0
  38. package/dist/tracks.js +48 -0
  39. package/dist/tracks.js.map +1 -0
  40. package/dist/ui/LearningMiniApp.d.ts +24 -0
  41. package/dist/ui/LearningMiniApp.d.ts.map +1 -0
  42. package/dist/ui/LearningMiniApp.js +80 -0
  43. package/dist/ui/LearningMiniApp.js.map +1 -0
  44. package/dist/ui/index.d.ts +2 -0
  45. package/dist/ui/index.js +3 -0
  46. package/example.ts +1 -0
  47. package/package.json +89 -0
  48. package/src/api-types.ts +43 -0
  49. package/src/api.test.ts +46 -0
  50. package/src/api.ts +301 -0
  51. package/src/docs/index.ts +1 -0
  52. package/src/docs/learning-journey-registry.docblock.ts +36 -0
  53. package/src/example.ts +24 -0
  54. package/src/index.ts +8 -0
  55. package/src/learning-journey-registry.feature.ts +64 -0
  56. package/src/presentations/index.ts +69 -0
  57. package/src/progress-store.ts +39 -0
  58. package/src/tracks.ts +91 -0
  59. package/src/ui/LearningMiniApp.tsx +121 -0
  60. package/src/ui/index.ts +5 -0
  61. package/tsconfig.json +9 -0
  62. package/tsconfig.tsbuildinfo +1 -0
  63. package/tsdown.config.js +17 -0
@@ -0,0 +1,36 @@
1
+ import type { DocBlock } from '@contractspec/lib.contracts/docs';
2
+ import { registerDocBlocks } from '@contractspec/lib.contracts/docs';
3
+
4
+ const registryDocBlocks: DocBlock[] = [
5
+ {
6
+ id: 'docs.learning-journey.registry',
7
+ title: 'Learning Journey — Example Track Registry',
8
+ summary:
9
+ 'Aggregates learning journey example tracks (Studio onboarding, Platform tour, CRM first win, Drills, Ambient Coach, Quest challenges).',
10
+ kind: 'usage',
11
+ visibility: 'public',
12
+ route: '/docs/learning-journey/registry',
13
+ tags: ['learning', 'registry', 'onboarding'],
14
+ body: `## Tracks
15
+ - \`studio_getting_started\` (Studio onboarding)
16
+ - \`platform_primitives_tour\` (Platform primitives)
17
+ - \`crm_first_win\` (CRM pipeline onboarding)
18
+ - \`drills_language_basics\` (Drills & SRS)
19
+ - \`money_ambient_coach\`, \`coliving_ambient_coach\` (Ambient tips)
20
+ - \`money_reset_7day\` (Quest/challenge)
21
+
22
+ ## Exports
23
+ - \`learningJourneyTracks\` — raw specs
24
+ - \`onboardingTrackCatalog\` — DTOs aligned with onboarding API
25
+ - \`mapTrackSpecToDto\` — helper to map individual tracks
26
+
27
+ ## Wiring
28
+ - Use with onboarding API contracts:
29
+ - \`learning.onboarding.listTracks\`
30
+ - \`learning.onboarding.getProgress\`
31
+ - \`learning.onboarding.recordEvent\`
32
+ - Intended for registry/adapters in Studio UI or services that surface tracks.`,
33
+ },
34
+ ];
35
+
36
+ registerDocBlocks(registryDocBlocks);
package/src/example.ts ADDED
@@ -0,0 +1,24 @@
1
+ const example = {
2
+ id: 'learning-journey-registry',
3
+ title: 'Learning Journey Registry',
4
+ summary:
5
+ 'Registry of learning journey tracks + presentations + UI mini-app bindings.',
6
+ tags: ['learning', 'journey', 'registry'],
7
+ kind: 'library',
8
+ visibility: 'public',
9
+ docs: {
10
+ rootDocId: 'docs.examples.learning-journey-registry',
11
+ },
12
+ entrypoints: {
13
+ packageName: '@contractspec/example.learning-journey-registry',
14
+ docs: './docs',
15
+ },
16
+ surfaces: {
17
+ templates: true,
18
+ sandbox: { enabled: true, modes: ['markdown', 'specs'] },
19
+ studio: { enabled: true, installable: true },
20
+ mcp: { enabled: true },
21
+ },
22
+ } as const;
23
+
24
+ export default example;
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import './docs';
2
+
3
+ export * from './tracks';
4
+ export * from './api';
5
+ export * from './presentations';
6
+ export * from './learning-journey-registry.feature';
7
+ export * from './ui';
8
+ export { default as example } from './example';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Learning Journey Registry Feature Module Specification
3
+ *
4
+ * Defines the feature module for the learning journey registry.
5
+ */
6
+ import type { FeatureModuleSpec } from '@contractspec/lib.contracts';
7
+
8
+ /**
9
+ * Learning Journey Registry feature module that bundles
10
+ * the shared presentations for learning journey tracks.
11
+ */
12
+ export const LearningJourneyRegistryFeature: FeatureModuleSpec = {
13
+ meta: {
14
+ key: 'learning-journey-registry',
15
+ version: 1,
16
+ title: 'Learning Journey Registry',
17
+ description:
18
+ 'Shared registry and presentations for learning journey tracks',
19
+ domain: 'learning-journey',
20
+ owners: ['@learning-team'],
21
+ tags: ['learning', 'journey', 'onboarding', 'registry'],
22
+ stability: 'experimental',
23
+ },
24
+
25
+ // No operations in the registry - it's presentation-only
26
+ operations: [],
27
+
28
+ // Events emitted by this feature
29
+ events: [],
30
+
31
+ // Presentations associated with this feature
32
+ presentations: [
33
+ { key: 'learning.journey.track_list', version: 1 },
34
+ { key: 'learning.journey.track_detail', version: 1 },
35
+ { key: 'learning.journey.progress_widget', version: 1 },
36
+ ],
37
+
38
+ // No op to presentation links for registry
39
+ opToPresentation: [],
40
+
41
+ // Target requirements for multi-surface rendering
42
+ presentationsTargets: [
43
+ {
44
+ key: 'learning.journey.track_list',
45
+ version: 1,
46
+ targets: ['react', 'markdown'],
47
+ },
48
+ {
49
+ key: 'learning.journey.track_detail',
50
+ version: 1,
51
+ targets: ['react', 'markdown', 'application/json'],
52
+ },
53
+ {
54
+ key: 'learning.journey.progress_widget',
55
+ version: 1,
56
+ targets: ['react'],
57
+ },
58
+ ],
59
+
60
+ // Capability requirements
61
+ capabilities: {
62
+ requires: [{ key: 'identity', version: 1 }],
63
+ },
64
+ };
@@ -0,0 +1,69 @@
1
+ import type {
2
+ PresentationSpecMeta,
3
+ PresentationSpec,
4
+ } from '@contractspec/lib.contracts';
5
+ import { StabilityEnum } from '@contractspec/lib.contracts';
6
+
7
+ const baseMeta: Pick<
8
+ PresentationSpecMeta,
9
+ 'domain' | 'owners' | 'tags' | 'title' | 'stability' | 'goal' | 'context'
10
+ > = {
11
+ domain: 'learning-journey',
12
+ title: 'Learning Journey',
13
+ owners: ['@learning-team'] as string[],
14
+ tags: ['learning', 'journey', 'onboarding'] as string[],
15
+ stability: StabilityEnum.Experimental,
16
+ goal: 'Progress through learning tracks',
17
+ context: 'Learning journey section',
18
+ };
19
+
20
+ export const LearningTrackListPresentation: PresentationSpec = {
21
+ meta: {
22
+ key: 'learning.journey.track_list',
23
+ version: 1,
24
+ description: 'List of learning journeys available to the learner.',
25
+ ...baseMeta,
26
+ },
27
+ source: {
28
+ type: 'component',
29
+ framework: 'react',
30
+ componentKey: 'LearningTrackList',
31
+ },
32
+ targets: ['react', 'markdown'],
33
+ };
34
+
35
+ export const LearningTrackDetailPresentation: PresentationSpec = {
36
+ meta: {
37
+ key: 'learning.journey.track_detail',
38
+ version: 1,
39
+ description: 'Track detail with steps and progress state.',
40
+ ...baseMeta,
41
+ },
42
+ source: {
43
+ type: 'component',
44
+ framework: 'react',
45
+ componentKey: 'LearningTrackDetail',
46
+ },
47
+ targets: ['react', 'markdown', 'application/json'],
48
+ };
49
+
50
+ export const LearningTrackProgressWidgetPresentation: PresentationSpec = {
51
+ meta: {
52
+ key: 'learning.journey.progress_widget',
53
+ version: 1,
54
+ description: 'Compact widget showing progress for active track.',
55
+ ...baseMeta,
56
+ },
57
+ source: {
58
+ type: 'component',
59
+ framework: 'react',
60
+ componentKey: 'LearningTrackProgressWidget',
61
+ },
62
+ targets: ['react'],
63
+ };
64
+
65
+ export const learningJourneyPresentations = [
66
+ LearningTrackListPresentation,
67
+ LearningTrackDetailPresentation,
68
+ LearningTrackProgressWidgetPresentation,
69
+ ];
@@ -0,0 +1,39 @@
1
+ import type { LearningJourneyTrackSpec } from '@contractspec/module.learning-journey/track-spec';
2
+
3
+ import type { TrackProgress } from './api-types';
4
+
5
+ export const progressStore = new Map<string, Map<string, TrackProgress>>();
6
+
7
+ export const getTrackResolver =
8
+ (tracks: LearningJourneyTrackSpec[]) =>
9
+ (trackId: string): LearningJourneyTrackSpec | undefined =>
10
+ tracks.find((t) => t.id === trackId);
11
+
12
+ export const getLearnerTracks = (learnerId: string) => {
13
+ const existing = progressStore.get(learnerId);
14
+ if (existing) return existing;
15
+ const map = new Map<string, TrackProgress>();
16
+ progressStore.set(learnerId, map);
17
+ return map;
18
+ };
19
+
20
+ export const initProgress = (
21
+ learnerId: string,
22
+ track: LearningJourneyTrackSpec
23
+ ): TrackProgress => ({
24
+ learnerId,
25
+ trackId: track.id,
26
+ progress: 0,
27
+ isCompleted: false,
28
+ xpEarned: 0,
29
+ steps: track.steps.map((step) => ({
30
+ id: step.id,
31
+ status: 'PENDING',
32
+ xpEarned: 0,
33
+ occurrences: 0,
34
+ masteryCount: 0,
35
+ })),
36
+ startedAt: undefined,
37
+ completedAt: undefined,
38
+ lastActivityAt: undefined,
39
+ });
package/src/tracks.ts ADDED
@@ -0,0 +1,91 @@
1
+ import type {
2
+ LearningJourneyStepSpec,
3
+ LearningJourneyTrackSpec,
4
+ StepAvailabilitySpec,
5
+ StepCompletionConditionSpec,
6
+ } from '@contractspec/module.learning-journey/track-spec';
7
+ import { crmLearningTracks } from '@contractspec/example.learning-journey-crm-onboarding/track';
8
+ import { drillTracks } from '@contractspec/example.learning-journey-duo-drills/track';
9
+ import { ambientCoachTracks } from '@contractspec/example.learning-journey-ambient-coach/track';
10
+ import { questTracks } from '@contractspec/example.learning-journey-quest-challenges/track';
11
+ import { platformLearningTracks } from '@contractspec/example.learning-journey-platform-tour/track';
12
+ import { studioLearningTracks } from '@contractspec/example.learning-journey-studio-onboarding/track';
13
+
14
+ export interface OnboardingStepDto {
15
+ id: string;
16
+ title: string;
17
+ description?: string;
18
+ completionEvent: string;
19
+ completionCondition?: StepCompletionConditionSpec;
20
+ xpReward?: number;
21
+ isRequired?: boolean;
22
+ canSkip?: boolean;
23
+ actionUrl?: string;
24
+ actionLabel?: string;
25
+ availability?: StepAvailabilitySpec;
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+
29
+ export interface OnboardingTrackDto {
30
+ id: string;
31
+ name: string;
32
+ description?: string;
33
+ productId?: string;
34
+ targetUserSegment?: string;
35
+ targetRole?: string;
36
+ totalXp?: number;
37
+ streakRule?: LearningJourneyTrackSpec['streakRule'];
38
+ completionRewards?: LearningJourneyTrackSpec['completionRewards'];
39
+ steps: OnboardingStepDto[];
40
+ metadata?: Record<string, unknown>;
41
+ }
42
+
43
+ const mapStep = (step: LearningJourneyStepSpec): OnboardingStepDto => ({
44
+ id: step.id,
45
+ title: step.title,
46
+ description: step.description,
47
+ completionEvent: step.completion.eventName,
48
+ completionCondition: step.completion,
49
+ xpReward: step.xpReward,
50
+ isRequired: step.isRequired,
51
+ canSkip: step.canSkip,
52
+ actionUrl: step.actionUrl,
53
+ actionLabel: step.actionLabel,
54
+ availability: step.availability,
55
+ metadata: step.metadata,
56
+ });
57
+
58
+ export const mapTrackSpecToDto = (
59
+ track: LearningJourneyTrackSpec
60
+ ): OnboardingTrackDto => ({
61
+ id: track.id,
62
+ name: track.name,
63
+ description: track.description,
64
+ productId: track.productId,
65
+ targetUserSegment: track.targetUserSegment,
66
+ targetRole: track.targetRole,
67
+ totalXp: track.totalXp,
68
+ streakRule: track.streakRule,
69
+ completionRewards: track.completionRewards,
70
+ steps: track.steps.map(mapStep),
71
+ metadata: track.metadata,
72
+ });
73
+
74
+ export const learningJourneyTracks: LearningJourneyTrackSpec[] = [
75
+ ...studioLearningTracks,
76
+ ...platformLearningTracks,
77
+ ...crmLearningTracks,
78
+ ...drillTracks,
79
+ ...ambientCoachTracks,
80
+ ...questTracks,
81
+ ];
82
+
83
+ export const onboardingTrackCatalog: OnboardingTrackDto[] =
84
+ learningJourneyTracks.map(mapTrackSpecToDto);
85
+
86
+ export {
87
+ studioLearningTracks,
88
+ platformLearningTracks,
89
+ crmLearningTracks,
90
+ mapStep,
91
+ };
@@ -0,0 +1,121 @@
1
+ 'use client';
2
+
3
+ import { useMemo } from 'react';
4
+ import { GamifiedMiniApp } from '@contractspec/example.learning-journey-ui-gamified';
5
+ import { OnboardingMiniApp } from '@contractspec/example.learning-journey-ui-onboarding';
6
+ import { CoachingMiniApp } from '@contractspec/example.learning-journey-ui-coaching';
7
+ import type { LearningView } from '@contractspec/example.learning-journey-ui-shared';
8
+ import { learningJourneyTracks } from '../tracks';
9
+
10
+ /** Template IDs that map to learning journey tracks */
11
+ type LearningTemplateId =
12
+ | 'learning-journey-duo-drills'
13
+ | 'learning-journey-quest-challenges'
14
+ | 'learning-journey-studio-onboarding'
15
+ | 'learning-journey-platform-tour'
16
+ | 'learning-journey-ambient-coach'
17
+ | 'learning-journey-crm-onboarding';
18
+
19
+ /** Map template IDs to track IDs */
20
+ const TEMPLATE_TO_TRACK: Record<LearningTemplateId, string> = {
21
+ 'learning-journey-duo-drills': 'drills_language_basics',
22
+ 'learning-journey-quest-challenges': 'money_reset_7day',
23
+ 'learning-journey-studio-onboarding': 'studio_getting_started',
24
+ 'learning-journey-platform-tour': 'platform_tour',
25
+ 'learning-journey-ambient-coach': 'money_ambient_coach',
26
+ 'learning-journey-crm-onboarding': 'crm_first_win',
27
+ };
28
+
29
+ /** Map template IDs to mini-app type */
30
+ const TEMPLATE_TO_APP_TYPE: Record<
31
+ LearningTemplateId,
32
+ 'gamified' | 'onboarding' | 'coaching'
33
+ > = {
34
+ 'learning-journey-duo-drills': 'gamified',
35
+ 'learning-journey-quest-challenges': 'gamified',
36
+ 'learning-journey-studio-onboarding': 'onboarding',
37
+ 'learning-journey-platform-tour': 'onboarding',
38
+ 'learning-journey-ambient-coach': 'coaching',
39
+ 'learning-journey-crm-onboarding': 'coaching',
40
+ };
41
+
42
+ interface LearningMiniAppProps {
43
+ templateId: string;
44
+ initialView?: LearningView;
45
+ onViewChange?: (view: LearningView) => void;
46
+ }
47
+
48
+ /** Router component that picks the correct mini-app based on template ID */
49
+ export function LearningMiniApp({
50
+ templateId,
51
+ initialView = 'overview',
52
+ onViewChange,
53
+ }: LearningMiniAppProps) {
54
+ // Find the track for this template
55
+ const track = useMemo(() => {
56
+ const trackId = TEMPLATE_TO_TRACK[templateId as LearningTemplateId];
57
+ if (!trackId) return null;
58
+ return learningJourneyTracks.find((t) => t.id === trackId);
59
+ }, [templateId]);
60
+
61
+ // Determine app type
62
+ const appType = TEMPLATE_TO_APP_TYPE[templateId as LearningTemplateId];
63
+
64
+ if (!track) {
65
+ return (
66
+ <div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-6 text-center">
67
+ <p className="text-amber-500">
68
+ Unknown learning template: {templateId}
69
+ </p>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ // Render the appropriate mini-app
75
+ switch (appType) {
76
+ case 'gamified':
77
+ return (
78
+ <GamifiedMiniApp
79
+ track={track}
80
+ initialView={initialView}
81
+ onViewChange={onViewChange}
82
+ />
83
+ );
84
+ case 'onboarding':
85
+ return (
86
+ <OnboardingMiniApp
87
+ track={track}
88
+ initialView={initialView}
89
+ onViewChange={onViewChange}
90
+ />
91
+ );
92
+ case 'coaching':
93
+ return (
94
+ <CoachingMiniApp
95
+ track={track}
96
+ initialView={initialView}
97
+ onViewChange={onViewChange}
98
+ />
99
+ );
100
+ default:
101
+ return (
102
+ <div className="rounded-lg border border-red-500/50 bg-red-500/10 p-6 text-center">
103
+ <p className="text-red-500">
104
+ Unknown app type for template: {templateId}
105
+ </p>
106
+ </div>
107
+ );
108
+ }
109
+ }
110
+
111
+ /** Check if a template ID is a learning journey template */
112
+ export function isLearningTemplate(
113
+ templateId: string
114
+ ): templateId is LearningTemplateId {
115
+ return templateId in TEMPLATE_TO_TRACK;
116
+ }
117
+
118
+ /** Get all learning template IDs */
119
+ export function getLearningTemplateIds(): LearningTemplateId[] {
120
+ return Object.keys(TEMPLATE_TO_TRACK) as LearningTemplateId[];
121
+ }
@@ -0,0 +1,5 @@
1
+ export {
2
+ LearningMiniApp,
3
+ isLearningTemplate,
4
+ getLearningTemplateIds,
5
+ } from './LearningMiniApp';
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@contractspec/tool.typescript/react-library.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules"],
5
+ "compilerOptions": {
6
+ "rootDir": "src",
7
+ "outDir": "dist"
8
+ }
9
+ }