timeback 0.1.5 → 0.1.6

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/dist/cli.js CHANGED
@@ -31623,7 +31623,7 @@ var TimebackSubject = exports_external2.enum([
31623
31623
  "Math",
31624
31624
  "None",
31625
31625
  "Other"
31626
- ]);
31626
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
31627
31627
  var TimebackGrade = exports_external2.union([
31628
31628
  exports_external2.literal(-1),
31629
31629
  exports_external2.literal(0),
@@ -31640,7 +31640,10 @@ var TimebackGrade = exports_external2.union([
31640
31640
  exports_external2.literal(11),
31641
31641
  exports_external2.literal(12),
31642
31642
  exports_external2.literal(13)
31643
- ]);
31643
+ ]).meta({
31644
+ id: "TimebackGrade",
31645
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
31646
+ });
31644
31647
  var ScoreStatus = exports_external2.enum([
31645
31648
  "exempt",
31646
31649
  "fully graded",
@@ -31870,62 +31873,84 @@ var CaliperListEventsParams = exports_external2.object({
31870
31873
  actorEmail: exports_external2.email().optional()
31871
31874
  }).strict();
31872
31875
  var CourseIds = exports_external2.object({
31873
- staging: exports_external2.string().optional(),
31874
- production: exports_external2.string().optional()
31875
- });
31876
- var CourseType = exports_external2.enum(["base", "hole-filling", "optional"]);
31877
- var PublishStatus = exports_external2.enum(["draft", "testing", "published", "deactivated"]);
31876
+ staging: exports_external2.string().meta({ description: "Course ID in staging environment" }).optional(),
31877
+ production: exports_external2.string().meta({ description: "Course ID in production environment" }).optional()
31878
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
31879
+ var CourseType = exports_external2.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
31880
+ var PublishStatus = exports_external2.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
31878
31881
  var CourseGoals = exports_external2.object({
31879
- dailyXp: exports_external2.number().int().positive().optional(),
31880
- dailyLessons: exports_external2.number().int().positive().optional(),
31881
- dailyActiveMinutes: exports_external2.number().int().positive().optional(),
31882
- dailyAccuracy: exports_external2.number().int().min(0).max(100).optional(),
31883
- dailyMasteredUnits: exports_external2.number().int().positive().optional()
31884
- });
31882
+ dailyXp: exports_external2.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
31883
+ dailyLessons: exports_external2.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
31884
+ dailyActiveMinutes: exports_external2.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
31885
+ dailyAccuracy: exports_external2.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
31886
+ dailyMasteredUnits: exports_external2.number().int().positive().meta({ description: "Target units to master per day" }).optional()
31887
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
31885
31888
  var CourseMetrics = exports_external2.object({
31886
- totalXp: exports_external2.number().int().positive().optional(),
31887
- totalLessons: exports_external2.number().int().positive().optional(),
31888
- totalGrades: exports_external2.number().int().positive().optional()
31889
- });
31889
+ totalXp: exports_external2.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
31890
+ totalLessons: exports_external2.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
31891
+ totalGrades: exports_external2.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
31892
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
31890
31893
  var CourseMetadata = exports_external2.object({
31891
31894
  courseType: CourseType.optional(),
31892
- isSupplemental: exports_external2.boolean().optional(),
31893
- isCustom: exports_external2.boolean().optional(),
31895
+ isSupplemental: exports_external2.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
31896
+ isCustom: exports_external2.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
31894
31897
  publishStatus: PublishStatus.optional(),
31895
- contactEmail: exports_external2.email().optional(),
31896
- primaryApp: exports_external2.string().optional(),
31898
+ contactEmail: exports_external2.email().meta({ description: "Contact email for course issues" }).optional(),
31899
+ primaryApp: exports_external2.string().meta({ description: "Primary application identifier" }).optional(),
31897
31900
  goals: CourseGoals.optional(),
31898
31901
  metrics: CourseMetrics.optional()
31899
- });
31902
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
31900
31903
  var CourseDefaults = exports_external2.object({
31901
- courseCode: exports_external2.string().optional(),
31902
- level: exports_external2.string().optional(),
31904
+ courseCode: exports_external2.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
31905
+ level: exports_external2.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
31903
31906
  metadata: CourseMetadata.optional()
31907
+ }).meta({
31908
+ id: "CourseDefaults",
31909
+ description: "Default properties that apply to all courses unless overridden"
31904
31910
  });
31905
31911
  var CourseEnvOverrides = exports_external2.object({
31906
- level: exports_external2.string().optional(),
31907
- sensor: exports_external2.string().url().optional(),
31908
- launchUrl: exports_external2.string().url().optional(),
31912
+ level: exports_external2.string().meta({ description: "Course level for this environment" }).optional(),
31913
+ sensor: exports_external2.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
31914
+ launchUrl: exports_external2.url().meta({ description: "LTI launch URL for this environment" }).optional(),
31909
31915
  metadata: CourseMetadata.optional()
31916
+ }).meta({
31917
+ id: "CourseEnvOverrides",
31918
+ description: "Environment-specific course overrides (non-identity fields)"
31910
31919
  });
31911
31920
  var CourseOverrides = exports_external2.object({
31912
- staging: CourseEnvOverrides.optional(),
31913
- production: CourseEnvOverrides.optional()
31914
- });
31921
+ staging: CourseEnvOverrides.meta({
31922
+ description: "Overrides for staging environment"
31923
+ }).optional(),
31924
+ production: CourseEnvOverrides.meta({
31925
+ description: "Overrides for production environment"
31926
+ }).optional()
31927
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
31915
31928
  var CourseConfig = CourseDefaults.extend({
31916
- subject: TimebackSubject,
31917
- grade: TimebackGrade.optional(),
31929
+ subject: TimebackSubject.meta({ description: "Subject area for this course" }),
31930
+ grade: TimebackGrade.meta({
31931
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
31932
+ }).optional(),
31918
31933
  ids: CourseIds.nullable().optional(),
31919
- sensor: exports_external2.string().url().optional(),
31920
- launchUrl: exports_external2.string().url().optional(),
31934
+ sensor: exports_external2.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
31935
+ launchUrl: exports_external2.url().meta({ description: "LTI launch URL for this course" }).optional(),
31921
31936
  overrides: CourseOverrides.optional()
31937
+ }).meta({
31938
+ id: "CourseConfig",
31939
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
31922
31940
  });
31923
31941
  var TimebackConfig = exports_external2.object({
31924
- name: exports_external2.string().min(1, "App name is required"),
31925
- defaults: CourseDefaults.optional(),
31926
- courses: exports_external2.array(CourseConfig).min(1, "At least one course is required"),
31927
- sensor: exports_external2.string().url().optional(),
31928
- launchUrl: exports_external2.string().url().optional()
31942
+ $schema: exports_external2.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
31943
+ name: exports_external2.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
31944
+ defaults: CourseDefaults.meta({
31945
+ description: "Default properties applied to all courses"
31946
+ }).optional(),
31947
+ courses: exports_external2.array(CourseConfig).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
31948
+ sensor: exports_external2.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
31949
+ launchUrl: exports_external2.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
31950
+ }).meta({
31951
+ id: "TimebackConfig",
31952
+ title: "Timeback Config",
31953
+ description: "Configuration schema for timeback.config.json files"
31929
31954
  }).refine((config22) => {
31930
31955
  return config22.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
31931
31956
  }, {
@@ -48001,7 +48026,7 @@ var TimebackSubject2 = exports_external22.enum([
48001
48026
  "Math",
48002
48027
  "None",
48003
48028
  "Other"
48004
- ]);
48029
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
48005
48030
  var TimebackGrade2 = exports_external22.union([
48006
48031
  exports_external22.literal(-1),
48007
48032
  exports_external22.literal(0),
@@ -48018,7 +48043,10 @@ var TimebackGrade2 = exports_external22.union([
48018
48043
  exports_external22.literal(11),
48019
48044
  exports_external22.literal(12),
48020
48045
  exports_external22.literal(13)
48021
- ]);
48046
+ ]).meta({
48047
+ id: "TimebackGrade",
48048
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
48049
+ });
48022
48050
  var ScoreStatus2 = exports_external22.enum([
48023
48051
  "exempt",
48024
48052
  "fully graded",
@@ -48248,62 +48276,84 @@ var CaliperListEventsParams2 = exports_external22.object({
48248
48276
  actorEmail: exports_external22.email().optional()
48249
48277
  }).strict();
48250
48278
  var CourseIds2 = exports_external22.object({
48251
- staging: exports_external22.string().optional(),
48252
- production: exports_external22.string().optional()
48253
- });
48254
- var CourseType2 = exports_external22.enum(["base", "hole-filling", "optional"]);
48255
- var PublishStatus2 = exports_external22.enum(["draft", "testing", "published", "deactivated"]);
48279
+ staging: exports_external22.string().meta({ description: "Course ID in staging environment" }).optional(),
48280
+ production: exports_external22.string().meta({ description: "Course ID in production environment" }).optional()
48281
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
48282
+ var CourseType2 = exports_external22.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
48283
+ var PublishStatus2 = exports_external22.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
48256
48284
  var CourseGoals2 = exports_external22.object({
48257
- dailyXp: exports_external22.number().int().positive().optional(),
48258
- dailyLessons: exports_external22.number().int().positive().optional(),
48259
- dailyActiveMinutes: exports_external22.number().int().positive().optional(),
48260
- dailyAccuracy: exports_external22.number().int().min(0).max(100).optional(),
48261
- dailyMasteredUnits: exports_external22.number().int().positive().optional()
48262
- });
48285
+ dailyXp: exports_external22.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
48286
+ dailyLessons: exports_external22.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
48287
+ dailyActiveMinutes: exports_external22.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
48288
+ dailyAccuracy: exports_external22.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
48289
+ dailyMasteredUnits: exports_external22.number().int().positive().meta({ description: "Target units to master per day" }).optional()
48290
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
48263
48291
  var CourseMetrics2 = exports_external22.object({
48264
- totalXp: exports_external22.number().int().positive().optional(),
48265
- totalLessons: exports_external22.number().int().positive().optional(),
48266
- totalGrades: exports_external22.number().int().positive().optional()
48267
- });
48292
+ totalXp: exports_external22.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
48293
+ totalLessons: exports_external22.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
48294
+ totalGrades: exports_external22.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
48295
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
48268
48296
  var CourseMetadata2 = exports_external22.object({
48269
48297
  courseType: CourseType2.optional(),
48270
- isSupplemental: exports_external22.boolean().optional(),
48271
- isCustom: exports_external22.boolean().optional(),
48298
+ isSupplemental: exports_external22.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
48299
+ isCustom: exports_external22.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
48272
48300
  publishStatus: PublishStatus2.optional(),
48273
- contactEmail: exports_external22.email().optional(),
48274
- primaryApp: exports_external22.string().optional(),
48301
+ contactEmail: exports_external22.email().meta({ description: "Contact email for course issues" }).optional(),
48302
+ primaryApp: exports_external22.string().meta({ description: "Primary application identifier" }).optional(),
48275
48303
  goals: CourseGoals2.optional(),
48276
48304
  metrics: CourseMetrics2.optional()
48277
- });
48305
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
48278
48306
  var CourseDefaults2 = exports_external22.object({
48279
- courseCode: exports_external22.string().optional(),
48280
- level: exports_external22.string().optional(),
48307
+ courseCode: exports_external22.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
48308
+ level: exports_external22.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
48281
48309
  metadata: CourseMetadata2.optional()
48310
+ }).meta({
48311
+ id: "CourseDefaults",
48312
+ description: "Default properties that apply to all courses unless overridden"
48282
48313
  });
48283
48314
  var CourseEnvOverrides2 = exports_external22.object({
48284
- level: exports_external22.string().optional(),
48285
- sensor: exports_external22.string().url().optional(),
48286
- launchUrl: exports_external22.string().url().optional(),
48315
+ level: exports_external22.string().meta({ description: "Course level for this environment" }).optional(),
48316
+ sensor: exports_external22.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
48317
+ launchUrl: exports_external22.url().meta({ description: "LTI launch URL for this environment" }).optional(),
48287
48318
  metadata: CourseMetadata2.optional()
48319
+ }).meta({
48320
+ id: "CourseEnvOverrides",
48321
+ description: "Environment-specific course overrides (non-identity fields)"
48288
48322
  });
48289
48323
  var CourseOverrides2 = exports_external22.object({
48290
- staging: CourseEnvOverrides2.optional(),
48291
- production: CourseEnvOverrides2.optional()
48292
- });
48324
+ staging: CourseEnvOverrides2.meta({
48325
+ description: "Overrides for staging environment"
48326
+ }).optional(),
48327
+ production: CourseEnvOverrides2.meta({
48328
+ description: "Overrides for production environment"
48329
+ }).optional()
48330
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
48293
48331
  var CourseConfig2 = CourseDefaults2.extend({
48294
- subject: TimebackSubject2,
48295
- grade: TimebackGrade2.optional(),
48332
+ subject: TimebackSubject2.meta({ description: "Subject area for this course" }),
48333
+ grade: TimebackGrade2.meta({
48334
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
48335
+ }).optional(),
48296
48336
  ids: CourseIds2.nullable().optional(),
48297
- sensor: exports_external22.string().url().optional(),
48298
- launchUrl: exports_external22.string().url().optional(),
48337
+ sensor: exports_external22.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
48338
+ launchUrl: exports_external22.url().meta({ description: "LTI launch URL for this course" }).optional(),
48299
48339
  overrides: CourseOverrides2.optional()
48340
+ }).meta({
48341
+ id: "CourseConfig",
48342
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
48300
48343
  });
48301
48344
  var TimebackConfig2 = exports_external22.object({
48302
- name: exports_external22.string().min(1, "App name is required"),
48303
- defaults: CourseDefaults2.optional(),
48304
- courses: exports_external22.array(CourseConfig2).min(1, "At least one course is required"),
48305
- sensor: exports_external22.string().url().optional(),
48306
- launchUrl: exports_external22.string().url().optional()
48345
+ $schema: exports_external22.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
48346
+ name: exports_external22.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
48347
+ defaults: CourseDefaults2.meta({
48348
+ description: "Default properties applied to all courses"
48349
+ }).optional(),
48350
+ courses: exports_external22.array(CourseConfig2).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
48351
+ sensor: exports_external22.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
48352
+ launchUrl: exports_external22.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
48353
+ }).meta({
48354
+ id: "TimebackConfig",
48355
+ title: "Timeback Config",
48356
+ description: "Configuration schema for timeback.config.json files"
48307
48357
  }).refine((config222) => {
48308
48358
  return config222.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
48309
48359
  }, {
@@ -65414,7 +65464,7 @@ var TimebackSubject3 = exports_external3.enum([
65414
65464
  "Math",
65415
65465
  "None",
65416
65466
  "Other"
65417
- ]);
65467
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
65418
65468
  var TimebackGrade3 = exports_external3.union([
65419
65469
  exports_external3.literal(-1),
65420
65470
  exports_external3.literal(0),
@@ -65431,7 +65481,10 @@ var TimebackGrade3 = exports_external3.union([
65431
65481
  exports_external3.literal(11),
65432
65482
  exports_external3.literal(12),
65433
65483
  exports_external3.literal(13)
65434
- ]);
65484
+ ]).meta({
65485
+ id: "TimebackGrade",
65486
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
65487
+ });
65435
65488
  var ScoreStatus3 = exports_external3.enum([
65436
65489
  "exempt",
65437
65490
  "fully graded",
@@ -65661,62 +65714,84 @@ var CaliperListEventsParams3 = exports_external3.object({
65661
65714
  actorEmail: exports_external3.email().optional()
65662
65715
  }).strict();
65663
65716
  var CourseIds3 = exports_external3.object({
65664
- staging: exports_external3.string().optional(),
65665
- production: exports_external3.string().optional()
65666
- });
65667
- var CourseType3 = exports_external3.enum(["base", "hole-filling", "optional"]);
65668
- var PublishStatus3 = exports_external3.enum(["draft", "testing", "published", "deactivated"]);
65717
+ staging: exports_external3.string().meta({ description: "Course ID in staging environment" }).optional(),
65718
+ production: exports_external3.string().meta({ description: "Course ID in production environment" }).optional()
65719
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
65720
+ var CourseType3 = exports_external3.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
65721
+ var PublishStatus3 = exports_external3.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
65669
65722
  var CourseGoals3 = exports_external3.object({
65670
- dailyXp: exports_external3.number().int().positive().optional(),
65671
- dailyLessons: exports_external3.number().int().positive().optional(),
65672
- dailyActiveMinutes: exports_external3.number().int().positive().optional(),
65673
- dailyAccuracy: exports_external3.number().int().min(0).max(100).optional(),
65674
- dailyMasteredUnits: exports_external3.number().int().positive().optional()
65675
- });
65723
+ dailyXp: exports_external3.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
65724
+ dailyLessons: exports_external3.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
65725
+ dailyActiveMinutes: exports_external3.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
65726
+ dailyAccuracy: exports_external3.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
65727
+ dailyMasteredUnits: exports_external3.number().int().positive().meta({ description: "Target units to master per day" }).optional()
65728
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
65676
65729
  var CourseMetrics3 = exports_external3.object({
65677
- totalXp: exports_external3.number().int().positive().optional(),
65678
- totalLessons: exports_external3.number().int().positive().optional(),
65679
- totalGrades: exports_external3.number().int().positive().optional()
65680
- });
65730
+ totalXp: exports_external3.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
65731
+ totalLessons: exports_external3.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
65732
+ totalGrades: exports_external3.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
65733
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
65681
65734
  var CourseMetadata3 = exports_external3.object({
65682
65735
  courseType: CourseType3.optional(),
65683
- isSupplemental: exports_external3.boolean().optional(),
65684
- isCustom: exports_external3.boolean().optional(),
65736
+ isSupplemental: exports_external3.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
65737
+ isCustom: exports_external3.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
65685
65738
  publishStatus: PublishStatus3.optional(),
65686
- contactEmail: exports_external3.email().optional(),
65687
- primaryApp: exports_external3.string().optional(),
65739
+ contactEmail: exports_external3.email().meta({ description: "Contact email for course issues" }).optional(),
65740
+ primaryApp: exports_external3.string().meta({ description: "Primary application identifier" }).optional(),
65688
65741
  goals: CourseGoals3.optional(),
65689
65742
  metrics: CourseMetrics3.optional()
65690
- });
65743
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
65691
65744
  var CourseDefaults3 = exports_external3.object({
65692
- courseCode: exports_external3.string().optional(),
65693
- level: exports_external3.string().optional(),
65745
+ courseCode: exports_external3.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
65746
+ level: exports_external3.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
65694
65747
  metadata: CourseMetadata3.optional()
65748
+ }).meta({
65749
+ id: "CourseDefaults",
65750
+ description: "Default properties that apply to all courses unless overridden"
65695
65751
  });
65696
65752
  var CourseEnvOverrides3 = exports_external3.object({
65697
- level: exports_external3.string().optional(),
65698
- sensor: exports_external3.string().url().optional(),
65699
- launchUrl: exports_external3.string().url().optional(),
65753
+ level: exports_external3.string().meta({ description: "Course level for this environment" }).optional(),
65754
+ sensor: exports_external3.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
65755
+ launchUrl: exports_external3.url().meta({ description: "LTI launch URL for this environment" }).optional(),
65700
65756
  metadata: CourseMetadata3.optional()
65757
+ }).meta({
65758
+ id: "CourseEnvOverrides",
65759
+ description: "Environment-specific course overrides (non-identity fields)"
65701
65760
  });
65702
65761
  var CourseOverrides3 = exports_external3.object({
65703
- staging: CourseEnvOverrides3.optional(),
65704
- production: CourseEnvOverrides3.optional()
65705
- });
65762
+ staging: CourseEnvOverrides3.meta({
65763
+ description: "Overrides for staging environment"
65764
+ }).optional(),
65765
+ production: CourseEnvOverrides3.meta({
65766
+ description: "Overrides for production environment"
65767
+ }).optional()
65768
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
65706
65769
  var CourseConfig3 = CourseDefaults3.extend({
65707
- subject: TimebackSubject3,
65708
- grade: TimebackGrade3.optional(),
65770
+ subject: TimebackSubject3.meta({ description: "Subject area for this course" }),
65771
+ grade: TimebackGrade3.meta({
65772
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
65773
+ }).optional(),
65709
65774
  ids: CourseIds3.nullable().optional(),
65710
- sensor: exports_external3.string().url().optional(),
65711
- launchUrl: exports_external3.string().url().optional(),
65775
+ sensor: exports_external3.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
65776
+ launchUrl: exports_external3.url().meta({ description: "LTI launch URL for this course" }).optional(),
65712
65777
  overrides: CourseOverrides3.optional()
65778
+ }).meta({
65779
+ id: "CourseConfig",
65780
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
65713
65781
  });
65714
65782
  var TimebackConfig3 = exports_external3.object({
65715
- name: exports_external3.string().min(1, "App name is required"),
65716
- defaults: CourseDefaults3.optional(),
65717
- courses: exports_external3.array(CourseConfig3).min(1, "At least one course is required"),
65718
- sensor: exports_external3.string().url().optional(),
65719
- launchUrl: exports_external3.string().url().optional()
65783
+ $schema: exports_external3.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
65784
+ name: exports_external3.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
65785
+ defaults: CourseDefaults3.meta({
65786
+ description: "Default properties applied to all courses"
65787
+ }).optional(),
65788
+ courses: exports_external3.array(CourseConfig3).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
65789
+ sensor: exports_external3.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
65790
+ launchUrl: exports_external3.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
65791
+ }).meta({
65792
+ id: "TimebackConfig",
65793
+ title: "Timeback Config",
65794
+ description: "Configuration schema for timeback.config.json files"
65720
65795
  }).refine((config222) => {
65721
65796
  return config222.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
65722
65797
  }, {
@@ -83021,7 +83096,7 @@ var TimebackSubject4 = exports_external4.enum([
83021
83096
  "Math",
83022
83097
  "None",
83023
83098
  "Other"
83024
- ]);
83099
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
83025
83100
  var TimebackGrade4 = exports_external4.union([
83026
83101
  exports_external4.literal(-1),
83027
83102
  exports_external4.literal(0),
@@ -83038,7 +83113,10 @@ var TimebackGrade4 = exports_external4.union([
83038
83113
  exports_external4.literal(11),
83039
83114
  exports_external4.literal(12),
83040
83115
  exports_external4.literal(13)
83041
- ]);
83116
+ ]).meta({
83117
+ id: "TimebackGrade",
83118
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
83119
+ });
83042
83120
  var ScoreStatus4 = exports_external4.enum([
83043
83121
  "exempt",
83044
83122
  "fully graded",
@@ -83268,62 +83346,84 @@ var CaliperListEventsParams4 = exports_external4.object({
83268
83346
  actorEmail: exports_external4.email().optional()
83269
83347
  }).strict();
83270
83348
  var CourseIds4 = exports_external4.object({
83271
- staging: exports_external4.string().optional(),
83272
- production: exports_external4.string().optional()
83273
- });
83274
- var CourseType4 = exports_external4.enum(["base", "hole-filling", "optional"]);
83275
- var PublishStatus4 = exports_external4.enum(["draft", "testing", "published", "deactivated"]);
83349
+ staging: exports_external4.string().meta({ description: "Course ID in staging environment" }).optional(),
83350
+ production: exports_external4.string().meta({ description: "Course ID in production environment" }).optional()
83351
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
83352
+ var CourseType4 = exports_external4.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
83353
+ var PublishStatus4 = exports_external4.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
83276
83354
  var CourseGoals4 = exports_external4.object({
83277
- dailyXp: exports_external4.number().int().positive().optional(),
83278
- dailyLessons: exports_external4.number().int().positive().optional(),
83279
- dailyActiveMinutes: exports_external4.number().int().positive().optional(),
83280
- dailyAccuracy: exports_external4.number().int().min(0).max(100).optional(),
83281
- dailyMasteredUnits: exports_external4.number().int().positive().optional()
83282
- });
83355
+ dailyXp: exports_external4.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
83356
+ dailyLessons: exports_external4.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
83357
+ dailyActiveMinutes: exports_external4.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
83358
+ dailyAccuracy: exports_external4.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
83359
+ dailyMasteredUnits: exports_external4.number().int().positive().meta({ description: "Target units to master per day" }).optional()
83360
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
83283
83361
  var CourseMetrics4 = exports_external4.object({
83284
- totalXp: exports_external4.number().int().positive().optional(),
83285
- totalLessons: exports_external4.number().int().positive().optional(),
83286
- totalGrades: exports_external4.number().int().positive().optional()
83287
- });
83362
+ totalXp: exports_external4.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
83363
+ totalLessons: exports_external4.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
83364
+ totalGrades: exports_external4.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
83365
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
83288
83366
  var CourseMetadata4 = exports_external4.object({
83289
83367
  courseType: CourseType4.optional(),
83290
- isSupplemental: exports_external4.boolean().optional(),
83291
- isCustom: exports_external4.boolean().optional(),
83368
+ isSupplemental: exports_external4.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
83369
+ isCustom: exports_external4.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
83292
83370
  publishStatus: PublishStatus4.optional(),
83293
- contactEmail: exports_external4.email().optional(),
83294
- primaryApp: exports_external4.string().optional(),
83371
+ contactEmail: exports_external4.email().meta({ description: "Contact email for course issues" }).optional(),
83372
+ primaryApp: exports_external4.string().meta({ description: "Primary application identifier" }).optional(),
83295
83373
  goals: CourseGoals4.optional(),
83296
83374
  metrics: CourseMetrics4.optional()
83297
- });
83375
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
83298
83376
  var CourseDefaults4 = exports_external4.object({
83299
- courseCode: exports_external4.string().optional(),
83300
- level: exports_external4.string().optional(),
83377
+ courseCode: exports_external4.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
83378
+ level: exports_external4.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
83301
83379
  metadata: CourseMetadata4.optional()
83380
+ }).meta({
83381
+ id: "CourseDefaults",
83382
+ description: "Default properties that apply to all courses unless overridden"
83302
83383
  });
83303
83384
  var CourseEnvOverrides4 = exports_external4.object({
83304
- level: exports_external4.string().optional(),
83305
- sensor: exports_external4.string().url().optional(),
83306
- launchUrl: exports_external4.string().url().optional(),
83385
+ level: exports_external4.string().meta({ description: "Course level for this environment" }).optional(),
83386
+ sensor: exports_external4.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
83387
+ launchUrl: exports_external4.url().meta({ description: "LTI launch URL for this environment" }).optional(),
83307
83388
  metadata: CourseMetadata4.optional()
83389
+ }).meta({
83390
+ id: "CourseEnvOverrides",
83391
+ description: "Environment-specific course overrides (non-identity fields)"
83308
83392
  });
83309
83393
  var CourseOverrides4 = exports_external4.object({
83310
- staging: CourseEnvOverrides4.optional(),
83311
- production: CourseEnvOverrides4.optional()
83312
- });
83394
+ staging: CourseEnvOverrides4.meta({
83395
+ description: "Overrides for staging environment"
83396
+ }).optional(),
83397
+ production: CourseEnvOverrides4.meta({
83398
+ description: "Overrides for production environment"
83399
+ }).optional()
83400
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
83313
83401
  var CourseConfig4 = CourseDefaults4.extend({
83314
- subject: TimebackSubject4,
83315
- grade: TimebackGrade4.optional(),
83402
+ subject: TimebackSubject4.meta({ description: "Subject area for this course" }),
83403
+ grade: TimebackGrade4.meta({
83404
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
83405
+ }).optional(),
83316
83406
  ids: CourseIds4.nullable().optional(),
83317
- sensor: exports_external4.string().url().optional(),
83318
- launchUrl: exports_external4.string().url().optional(),
83407
+ sensor: exports_external4.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
83408
+ launchUrl: exports_external4.url().meta({ description: "LTI launch URL for this course" }).optional(),
83319
83409
  overrides: CourseOverrides4.optional()
83410
+ }).meta({
83411
+ id: "CourseConfig",
83412
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
83320
83413
  });
83321
83414
  var TimebackConfig4 = exports_external4.object({
83322
- name: exports_external4.string().min(1, "App name is required"),
83323
- defaults: CourseDefaults4.optional(),
83324
- courses: exports_external4.array(CourseConfig4).min(1, "At least one course is required"),
83325
- sensor: exports_external4.string().url().optional(),
83326
- launchUrl: exports_external4.string().url().optional()
83415
+ $schema: exports_external4.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
83416
+ name: exports_external4.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
83417
+ defaults: CourseDefaults4.meta({
83418
+ description: "Default properties applied to all courses"
83419
+ }).optional(),
83420
+ courses: exports_external4.array(CourseConfig4).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
83421
+ sensor: exports_external4.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
83422
+ launchUrl: exports_external4.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
83423
+ }).meta({
83424
+ id: "TimebackConfig",
83425
+ title: "Timeback Config",
83426
+ description: "Configuration schema for timeback.config.json files"
83327
83427
  }).refine((config222) => {
83328
83428
  return config222.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
83329
83429
  }, {
@@ -99658,7 +99758,7 @@ var TimebackSubject5 = exports_external5.enum([
99658
99758
  "Math",
99659
99759
  "None",
99660
99760
  "Other"
99661
- ]);
99761
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
99662
99762
  var TimebackGrade5 = exports_external5.union([
99663
99763
  exports_external5.literal(-1),
99664
99764
  exports_external5.literal(0),
@@ -99675,7 +99775,10 @@ var TimebackGrade5 = exports_external5.union([
99675
99775
  exports_external5.literal(11),
99676
99776
  exports_external5.literal(12),
99677
99777
  exports_external5.literal(13)
99678
- ]);
99778
+ ]).meta({
99779
+ id: "TimebackGrade",
99780
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
99781
+ });
99679
99782
  var ScoreStatus5 = exports_external5.enum([
99680
99783
  "exempt",
99681
99784
  "fully graded",
@@ -99905,62 +100008,84 @@ var CaliperListEventsParams5 = exports_external5.object({
99905
100008
  actorEmail: exports_external5.email().optional()
99906
100009
  }).strict();
99907
100010
  var CourseIds5 = exports_external5.object({
99908
- staging: exports_external5.string().optional(),
99909
- production: exports_external5.string().optional()
99910
- });
99911
- var CourseType5 = exports_external5.enum(["base", "hole-filling", "optional"]);
99912
- var PublishStatus5 = exports_external5.enum(["draft", "testing", "published", "deactivated"]);
100011
+ staging: exports_external5.string().meta({ description: "Course ID in staging environment" }).optional(),
100012
+ production: exports_external5.string().meta({ description: "Course ID in production environment" }).optional()
100013
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
100014
+ var CourseType5 = exports_external5.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
100015
+ var PublishStatus5 = exports_external5.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
99913
100016
  var CourseGoals5 = exports_external5.object({
99914
- dailyXp: exports_external5.number().int().positive().optional(),
99915
- dailyLessons: exports_external5.number().int().positive().optional(),
99916
- dailyActiveMinutes: exports_external5.number().int().positive().optional(),
99917
- dailyAccuracy: exports_external5.number().int().min(0).max(100).optional(),
99918
- dailyMasteredUnits: exports_external5.number().int().positive().optional()
99919
- });
100017
+ dailyXp: exports_external5.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
100018
+ dailyLessons: exports_external5.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
100019
+ dailyActiveMinutes: exports_external5.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
100020
+ dailyAccuracy: exports_external5.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
100021
+ dailyMasteredUnits: exports_external5.number().int().positive().meta({ description: "Target units to master per day" }).optional()
100022
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
99920
100023
  var CourseMetrics5 = exports_external5.object({
99921
- totalXp: exports_external5.number().int().positive().optional(),
99922
- totalLessons: exports_external5.number().int().positive().optional(),
99923
- totalGrades: exports_external5.number().int().positive().optional()
99924
- });
100024
+ totalXp: exports_external5.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
100025
+ totalLessons: exports_external5.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
100026
+ totalGrades: exports_external5.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
100027
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
99925
100028
  var CourseMetadata5 = exports_external5.object({
99926
100029
  courseType: CourseType5.optional(),
99927
- isSupplemental: exports_external5.boolean().optional(),
99928
- isCustom: exports_external5.boolean().optional(),
100030
+ isSupplemental: exports_external5.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
100031
+ isCustom: exports_external5.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
99929
100032
  publishStatus: PublishStatus5.optional(),
99930
- contactEmail: exports_external5.email().optional(),
99931
- primaryApp: exports_external5.string().optional(),
100033
+ contactEmail: exports_external5.email().meta({ description: "Contact email for course issues" }).optional(),
100034
+ primaryApp: exports_external5.string().meta({ description: "Primary application identifier" }).optional(),
99932
100035
  goals: CourseGoals5.optional(),
99933
100036
  metrics: CourseMetrics5.optional()
99934
- });
100037
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
99935
100038
  var CourseDefaults5 = exports_external5.object({
99936
- courseCode: exports_external5.string().optional(),
99937
- level: exports_external5.string().optional(),
100039
+ courseCode: exports_external5.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
100040
+ level: exports_external5.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
99938
100041
  metadata: CourseMetadata5.optional()
100042
+ }).meta({
100043
+ id: "CourseDefaults",
100044
+ description: "Default properties that apply to all courses unless overridden"
99939
100045
  });
99940
100046
  var CourseEnvOverrides5 = exports_external5.object({
99941
- level: exports_external5.string().optional(),
99942
- sensor: exports_external5.string().url().optional(),
99943
- launchUrl: exports_external5.string().url().optional(),
100047
+ level: exports_external5.string().meta({ description: "Course level for this environment" }).optional(),
100048
+ sensor: exports_external5.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
100049
+ launchUrl: exports_external5.url().meta({ description: "LTI launch URL for this environment" }).optional(),
99944
100050
  metadata: CourseMetadata5.optional()
100051
+ }).meta({
100052
+ id: "CourseEnvOverrides",
100053
+ description: "Environment-specific course overrides (non-identity fields)"
99945
100054
  });
99946
100055
  var CourseOverrides5 = exports_external5.object({
99947
- staging: CourseEnvOverrides5.optional(),
99948
- production: CourseEnvOverrides5.optional()
99949
- });
100056
+ staging: CourseEnvOverrides5.meta({
100057
+ description: "Overrides for staging environment"
100058
+ }).optional(),
100059
+ production: CourseEnvOverrides5.meta({
100060
+ description: "Overrides for production environment"
100061
+ }).optional()
100062
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
99950
100063
  var CourseConfig5 = CourseDefaults5.extend({
99951
- subject: TimebackSubject5,
99952
- grade: TimebackGrade5.optional(),
100064
+ subject: TimebackSubject5.meta({ description: "Subject area for this course" }),
100065
+ grade: TimebackGrade5.meta({
100066
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
100067
+ }).optional(),
99953
100068
  ids: CourseIds5.nullable().optional(),
99954
- sensor: exports_external5.string().url().optional(),
99955
- launchUrl: exports_external5.string().url().optional(),
100069
+ sensor: exports_external5.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
100070
+ launchUrl: exports_external5.url().meta({ description: "LTI launch URL for this course" }).optional(),
99956
100071
  overrides: CourseOverrides5.optional()
100072
+ }).meta({
100073
+ id: "CourseConfig",
100074
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
99957
100075
  });
99958
100076
  var TimebackConfig5 = exports_external5.object({
99959
- name: exports_external5.string().min(1, "App name is required"),
99960
- defaults: CourseDefaults5.optional(),
99961
- courses: exports_external5.array(CourseConfig5).min(1, "At least one course is required"),
99962
- sensor: exports_external5.string().url().optional(),
99963
- launchUrl: exports_external5.string().url().optional()
100077
+ $schema: exports_external5.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
100078
+ name: exports_external5.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
100079
+ defaults: CourseDefaults5.meta({
100080
+ description: "Default properties applied to all courses"
100081
+ }).optional(),
100082
+ courses: exports_external5.array(CourseConfig5).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
100083
+ sensor: exports_external5.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
100084
+ launchUrl: exports_external5.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
100085
+ }).meta({
100086
+ id: "TimebackConfig",
100087
+ title: "Timeback Config",
100088
+ description: "Configuration schema for timeback.config.json files"
99964
100089
  }).refine((config222) => {
99965
100090
  return config222.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
99966
100091
  }, {
@@ -101840,8 +101965,8 @@ var outro = {
101840
101965
  };
101841
101966
  // ../internal/cli-infra/src/config/timeback.ts
101842
101967
  import { existsSync } from "node:fs";
101843
- import { basename, relative, resolve } from "node:path";
101844
- import { pathToFileURL } from "node:url";
101968
+ import { basename, extname, relative, resolve } from "node:path";
101969
+ import { loadConfig as c12LoadConfig } from "c12";
101845
101970
  // ../types/src/zod/primitives.ts
101846
101971
  var IsoDateTimeRegex6 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
101847
101972
  var IsoDateTimeString6 = exports_external.string().min(1).regex(IsoDateTimeRegex6, "must be a valid ISO 8601 datetime");
@@ -101857,7 +101982,7 @@ var TimebackSubject6 = exports_external.enum([
101857
101982
  "Math",
101858
101983
  "None",
101859
101984
  "Other"
101860
- ]);
101985
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
101861
101986
  var TimebackGrade6 = exports_external.union([
101862
101987
  exports_external.literal(-1),
101863
101988
  exports_external.literal(0),
@@ -101874,7 +101999,10 @@ var TimebackGrade6 = exports_external.union([
101874
101999
  exports_external.literal(11),
101875
102000
  exports_external.literal(12),
101876
102001
  exports_external.literal(13)
101877
- ]);
102002
+ ]).meta({
102003
+ id: "TimebackGrade",
102004
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
102005
+ });
101878
102006
  var ScoreStatus6 = exports_external.enum([
101879
102007
  "exempt",
101880
102008
  "fully graded",
@@ -102106,62 +102234,84 @@ var CaliperListEventsParams6 = exports_external.object({
102106
102234
  }).strict();
102107
102235
  // ../types/src/zod/config.ts
102108
102236
  var CourseIds6 = exports_external.object({
102109
- staging: exports_external.string().optional(),
102110
- production: exports_external.string().optional()
102111
- });
102112
- var CourseType6 = exports_external.enum(["base", "hole-filling", "optional"]);
102113
- var PublishStatus6 = exports_external.enum(["draft", "testing", "published", "deactivated"]);
102237
+ staging: exports_external.string().meta({ description: "Course ID in staging environment" }).optional(),
102238
+ production: exports_external.string().meta({ description: "Course ID in production environment" }).optional()
102239
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
102240
+ var CourseType6 = exports_external.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
102241
+ var PublishStatus6 = exports_external.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
102114
102242
  var CourseGoals6 = exports_external.object({
102115
- dailyXp: exports_external.number().int().positive().optional(),
102116
- dailyLessons: exports_external.number().int().positive().optional(),
102117
- dailyActiveMinutes: exports_external.number().int().positive().optional(),
102118
- dailyAccuracy: exports_external.number().int().min(0).max(100).optional(),
102119
- dailyMasteredUnits: exports_external.number().int().positive().optional()
102120
- });
102243
+ dailyXp: exports_external.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
102244
+ dailyLessons: exports_external.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
102245
+ dailyActiveMinutes: exports_external.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
102246
+ dailyAccuracy: exports_external.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
102247
+ dailyMasteredUnits: exports_external.number().int().positive().meta({ description: "Target units to master per day" }).optional()
102248
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
102121
102249
  var CourseMetrics6 = exports_external.object({
102122
- totalXp: exports_external.number().int().positive().optional(),
102123
- totalLessons: exports_external.number().int().positive().optional(),
102124
- totalGrades: exports_external.number().int().positive().optional()
102125
- });
102250
+ totalXp: exports_external.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
102251
+ totalLessons: exports_external.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
102252
+ totalGrades: exports_external.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
102253
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
102126
102254
  var CourseMetadata6 = exports_external.object({
102127
102255
  courseType: CourseType6.optional(),
102128
- isSupplemental: exports_external.boolean().optional(),
102129
- isCustom: exports_external.boolean().optional(),
102256
+ isSupplemental: exports_external.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
102257
+ isCustom: exports_external.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
102130
102258
  publishStatus: PublishStatus6.optional(),
102131
- contactEmail: exports_external.email().optional(),
102132
- primaryApp: exports_external.string().optional(),
102259
+ contactEmail: exports_external.email().meta({ description: "Contact email for course issues" }).optional(),
102260
+ primaryApp: exports_external.string().meta({ description: "Primary application identifier" }).optional(),
102133
102261
  goals: CourseGoals6.optional(),
102134
102262
  metrics: CourseMetrics6.optional()
102135
- });
102263
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
102136
102264
  var CourseDefaults6 = exports_external.object({
102137
- courseCode: exports_external.string().optional(),
102138
- level: exports_external.string().optional(),
102265
+ courseCode: exports_external.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
102266
+ level: exports_external.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
102139
102267
  metadata: CourseMetadata6.optional()
102268
+ }).meta({
102269
+ id: "CourseDefaults",
102270
+ description: "Default properties that apply to all courses unless overridden"
102140
102271
  });
102141
102272
  var CourseEnvOverrides6 = exports_external.object({
102142
- level: exports_external.string().optional(),
102143
- sensor: exports_external.string().url().optional(),
102144
- launchUrl: exports_external.string().url().optional(),
102273
+ level: exports_external.string().meta({ description: "Course level for this environment" }).optional(),
102274
+ sensor: exports_external.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
102275
+ launchUrl: exports_external.url().meta({ description: "LTI launch URL for this environment" }).optional(),
102145
102276
  metadata: CourseMetadata6.optional()
102277
+ }).meta({
102278
+ id: "CourseEnvOverrides",
102279
+ description: "Environment-specific course overrides (non-identity fields)"
102146
102280
  });
102147
102281
  var CourseOverrides6 = exports_external.object({
102148
- staging: CourseEnvOverrides6.optional(),
102149
- production: CourseEnvOverrides6.optional()
102150
- });
102282
+ staging: CourseEnvOverrides6.meta({
102283
+ description: "Overrides for staging environment"
102284
+ }).optional(),
102285
+ production: CourseEnvOverrides6.meta({
102286
+ description: "Overrides for production environment"
102287
+ }).optional()
102288
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
102151
102289
  var CourseConfig6 = CourseDefaults6.extend({
102152
- subject: TimebackSubject6,
102153
- grade: TimebackGrade6.optional(),
102290
+ subject: TimebackSubject6.meta({ description: "Subject area for this course" }),
102291
+ grade: TimebackGrade6.meta({
102292
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
102293
+ }).optional(),
102154
102294
  ids: CourseIds6.nullable().optional(),
102155
- sensor: exports_external.string().url().optional(),
102156
- launchUrl: exports_external.string().url().optional(),
102295
+ sensor: exports_external.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
102296
+ launchUrl: exports_external.url().meta({ description: "LTI launch URL for this course" }).optional(),
102157
102297
  overrides: CourseOverrides6.optional()
102298
+ }).meta({
102299
+ id: "CourseConfig",
102300
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
102158
102301
  });
102159
102302
  var TimebackConfig6 = exports_external.object({
102160
- name: exports_external.string().min(1, "App name is required"),
102161
- defaults: CourseDefaults6.optional(),
102162
- courses: exports_external.array(CourseConfig6).min(1, "At least one course is required"),
102163
- sensor: exports_external.string().url().optional(),
102164
- launchUrl: exports_external.string().url().optional()
102303
+ $schema: exports_external.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
102304
+ name: exports_external.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
102305
+ defaults: CourseDefaults6.meta({
102306
+ description: "Default properties applied to all courses"
102307
+ }).optional(),
102308
+ courses: exports_external.array(CourseConfig6).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
102309
+ sensor: exports_external.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
102310
+ launchUrl: exports_external.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
102311
+ }).meta({
102312
+ id: "TimebackConfig",
102313
+ title: "Timeback Config",
102314
+ description: "Configuration schema for timeback.config.json files"
102165
102315
  }).refine((config6) => {
102166
102316
  return config6.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
102167
102317
  }, {
@@ -103178,94 +103328,88 @@ var QtiLessonFeedbackInput6 = exports_external.object({
103178
103328
  humanApproved: exports_external.boolean().optional()
103179
103329
  }).strict();
103180
103330
  // ../internal/cli-infra/src/config/timeback.ts
103181
- var FILE_PATTERNS = ["timeback.config.ts", "timeback.config.js", "timeback.config.mjs"];
103182
- var isBun = typeof globalThis.Bun !== "undefined";
103183
- var jitiInstance = null;
103184
- async function getJiti() {
103185
- if (jitiInstance)
103186
- return jitiInstance;
103187
- const { createJiti } = await import("jiti");
103188
- jitiInstance = createJiti(import.meta.url, { fsCache: false });
103189
- return jitiInstance;
103190
- }
103191
- async function importModule(fullPath) {
103192
- let module;
103193
- if (isBun) {
103194
- const fileUrl = pathToFileURL(fullPath).href;
103195
- module = await import(fileUrl);
103196
- } else {
103197
- const jiti = await getJiti();
103198
- module = await jiti.import(fullPath);
103331
+ var CONFIG_FILENAME = "timeback.config.json";
103332
+ function isJsonConfigPath(configPath) {
103333
+ return extname(configPath).toLowerCase() === ".json";
103334
+ }
103335
+ async function loadWithC12(cwd, configPath) {
103336
+ if (configPath && !isJsonConfigPath(configPath)) {
103337
+ throw new Error(`Config file must be JSON (.json): ${configPath}`);
103338
+ }
103339
+ const result = await c12LoadConfig({
103340
+ cwd,
103341
+ name: "timeback",
103342
+ configFile: configPath ?? CONFIG_FILENAME,
103343
+ rcFile: false,
103344
+ packageJson: false,
103345
+ dotenv: false,
103346
+ envName: false,
103347
+ extend: false,
103348
+ omit$Keys: true,
103349
+ defaults: {},
103350
+ overrides: {}
103351
+ });
103352
+ if (!result.config || Object.keys(result.config).length === 0) {
103353
+ return null;
103199
103354
  }
103200
- return module.default ?? module;
103355
+ const rawConfig = result.config;
103356
+ if ("extends" in rawConfig) {
103357
+ throw new Error("The 'extends' feature is not supported in timeback.config.json. " + "Please inline all configuration.");
103358
+ }
103359
+ const { $schema: _schema, ...configWithoutSchema } = rawConfig;
103360
+ return {
103361
+ config: configWithoutSchema,
103362
+ configFile: result.configFile ?? resolve(cwd, configPath ?? CONFIG_FILENAME)
103363
+ };
103201
103364
  }
103202
103365
  async function loadConfig(opts = {}) {
103203
103366
  const cwd = process.cwd();
103204
- let rawConfig = null;
103205
- let foundPath = null;
103206
- let loadError = null;
103207
- if (opts.configPath) {
103208
- const fullPath = resolve(cwd, opts.configPath);
103209
- if (!existsSync(fullPath)) {
103367
+ try {
103368
+ if (opts.configPath && !isJsonConfigPath(opts.configPath)) {
103210
103369
  return {
103211
103370
  success: false,
103212
- error: `Config file not found: ${opts.configPath}`
103371
+ error: `Config file must be JSON (.json): ${opts.configPath}`
103213
103372
  };
103214
103373
  }
103215
- try {
103216
- rawConfig = await importModule(fullPath);
103217
- foundPath = fullPath;
103218
- } catch (err) {
103374
+ if (opts.configPath) {
103375
+ const fullPath = resolve(cwd, opts.configPath);
103376
+ if (!existsSync(fullPath)) {
103377
+ return {
103378
+ success: false,
103379
+ error: `Config file not found: ${opts.configPath}`
103380
+ };
103381
+ }
103382
+ }
103383
+ const loaded = await loadWithC12(cwd, opts.configPath);
103384
+ if (!loaded) {
103219
103385
  return {
103220
103386
  success: false,
103221
- error: `Failed to load ${opts.configPath}:
103222
- ${err instanceof Error ? err.message : String(err)}`
103387
+ error: `No timeback config found. Run ${greenBright("timeback init")} to create one.`
103223
103388
  };
103224
103389
  }
103225
- } else {
103226
- for (const filename of FILE_PATTERNS) {
103227
- const fullPath = resolve(cwd, filename);
103228
- if (!existsSync(fullPath))
103229
- continue;
103230
- try {
103231
- rawConfig = await importModule(fullPath);
103232
- foundPath = fullPath;
103233
- break;
103234
- } catch (err) {
103235
- loadError = err instanceof Error ? err : new Error(String(err));
103236
- foundPath = fullPath;
103237
- break;
103238
- }
103390
+ const result = TimebackConfig6.safeParse(loaded.config);
103391
+ if (!result.success) {
103392
+ const issues = result.error.issues.map((issue4) => ` - ${issue4.path.join(".")}: ${issue4.message}`).join(`
103393
+ `);
103394
+ return {
103395
+ success: false,
103396
+ error: `Invalid config in ${basename(loaded.configFile)}:
103397
+ ${issues}`
103398
+ };
103239
103399
  }
103240
- }
103241
- if (loadError && foundPath) {
103242
103400
  return {
103243
- success: false,
103244
- error: `Failed to load ${basename(foundPath)}:
103245
- ${loadError.message}`
103246
- };
103247
- }
103248
- if (!rawConfig || !foundPath) {
103249
- return {
103250
- success: false,
103251
- error: `No timeback config found. Run ${greenBright("timeback init")} to create one.`
103401
+ success: true,
103402
+ config: result.data,
103403
+ configPath: loaded.configFile
103252
103404
  };
103253
- }
103254
- const result = TimebackConfig6.safeParse(rawConfig);
103255
- if (!result.success) {
103256
- const issues = result.error.issues.map((issue4) => ` - ${issue4.path.join(".")}: ${issue4.message}`).join(`
103257
- `);
103405
+ } catch (err) {
103406
+ const filename = opts.configPath ? basename(opts.configPath) : CONFIG_FILENAME;
103258
103407
  return {
103259
103408
  success: false,
103260
- error: `Invalid config in ${basename(foundPath)}:
103261
- ${issues}`
103409
+ error: `Failed to load ${filename}:
103410
+ ${err instanceof Error ? err.message : String(err)}`
103262
103411
  };
103263
103412
  }
103264
- return {
103265
- success: true,
103266
- config: result.data,
103267
- configPath: foundPath
103268
- };
103269
103413
  }
103270
103414
  function getRelativeConfigPath(configPath) {
103271
103415
  return relative(process.cwd(), configPath);
@@ -103530,42 +103674,9 @@ async function promptLaunchUrl() {
103530
103674
  }
103531
103675
  return input.trim();
103532
103676
  }
103533
- // ../internal/cli-infra/src/project/package-json.ts
103677
+ // ../internal/cli-infra/src/project/package-manager.ts
103534
103678
  import { existsSync as existsSync2 } from "node:fs";
103535
- import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
103536
103679
  import { resolve as resolve2 } from "node:path";
103537
- function hasWorkspaceSpec(record6) {
103538
- if (!record6)
103539
- return false;
103540
- return Object.values(record6).some((v2) => v2.startsWith("workspace:"));
103541
- }
103542
- function isWorkspaceProject(pkg) {
103543
- if (!pkg || typeof pkg !== "object")
103544
- return false;
103545
- const maybePkg = pkg;
103546
- return !!maybePkg.workspaces || hasWorkspaceSpec(maybePkg.dependencies) || hasWorkspaceSpec(maybePkg.devDependencies) || hasWorkspaceSpec(maybePkg.peerDependencies);
103547
- }
103548
- async function ensureDependency(options) {
103549
- const { cwd = process.cwd(), packageName, section, version: version6 } = options;
103550
- const packageJsonPath = resolve2(cwd, "package.json");
103551
- if (!existsSync2(packageJsonPath)) {
103552
- return { updated: false, reason: "no_package_json" };
103553
- }
103554
- const raw = await readFile2(packageJsonPath, "utf-8");
103555
- const pkg = JSON.parse(raw);
103556
- if (pkg.dependencies?.[packageName] || pkg.devDependencies?.[packageName]) {
103557
- return { updated: false, reason: "already_present" };
103558
- }
103559
- const chosen = version6 ?? (isWorkspaceProject(pkg) ? "workspace:*" : "*");
103560
- pkg[section] ??= {};
103561
- pkg[section][packageName] = chosen;
103562
- await writeFile2(packageJsonPath, JSON.stringify(pkg, null, 2) + `
103563
- `, "utf-8");
103564
- return { updated: true, reason: "updated", version: chosen };
103565
- }
103566
- // ../internal/cli-infra/src/project/package-manager.ts
103567
- import { existsSync as existsSync3 } from "node:fs";
103568
- import { resolve as resolve3 } from "node:path";
103569
103680
  function detectPackageManager(cwd) {
103570
103681
  const candidates = [
103571
103682
  { pm: "bun", file: "bun.lockb" },
@@ -103575,25 +103686,12 @@ function detectPackageManager(cwd) {
103575
103686
  { pm: "npm", file: "package-lock.json" }
103576
103687
  ];
103577
103688
  for (const { pm, file: file6 } of candidates) {
103578
- if (existsSync3(resolve3(cwd, file6))) {
103689
+ if (existsSync2(resolve2(cwd, file6))) {
103579
103690
  return { packageManager: pm, reason: "lockfile", lockfile: file6 };
103580
103691
  }
103581
103692
  }
103582
103693
  return { packageManager: "npm", reason: "default" };
103583
103694
  }
103584
- function getInstallCommand(pm) {
103585
- switch (pm) {
103586
- case "bun":
103587
- return { cmd: "bun", args: ["install"] };
103588
- case "pnpm":
103589
- return { cmd: "pnpm", args: ["install"] };
103590
- case "yarn":
103591
- return { cmd: "yarn", args: ["install"] };
103592
- case "npm":
103593
- default:
103594
- return { cmd: "npm", args: ["install"] };
103595
- }
103596
- }
103597
103695
  function getDlxCommand(pm, tool) {
103598
103696
  switch (pm) {
103599
103697
  case "bun":
@@ -103674,7 +103772,7 @@ function getAuthContext(env2) {
103674
103772
  };
103675
103773
  }
103676
103774
  // src/lib/common.ts
103677
- import { readFile as readFile3 } from "node:fs/promises";
103775
+ import { readFile as readFile2 } from "node:fs/promises";
103678
103776
 
103679
103777
  // ../internal/constants/src/endpoints.ts
103680
103778
  var DEFAULT_PLATFORM7 = "BEYOND_AI";
@@ -104460,7 +104558,7 @@ async function parseInputData(opts) {
104460
104558
  }
104461
104559
  if (opts.file) {
104462
104560
  try {
104463
- const content = await readFile3(opts.file, "utf-8");
104561
+ const content = await readFile2(opts.file, "utf-8");
104464
104562
  const parsed = JSON.parse(content);
104465
104563
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
104466
104564
  throw new Error(`File ${opts.file} must contain a JSON object, not an array or primitive`);
@@ -104532,32 +104630,10 @@ function getSyncedCourses(course) {
104532
104630
  return [];
104533
104631
  return Object.keys(course.ids).filter((env2) => course.ids?.[env2]).map((env2) => ({ env: env2, courseId: course.ids[env2] }));
104534
104632
  }
104535
- // src/lib/config/dependencies.ts
104536
- async function ensureSdkDependency(options) {
104537
- const result = await ensureDependency({
104538
- cwd: options.cwd,
104539
- packageName: "@timeback/sdk",
104540
- section: "dependencies"
104541
- });
104542
- if (result.reason === "updated") {
104543
- options.logger.success(`Added @timeback/sdk@${result.version} to package.json`);
104544
- return { updated: true, reason: "updated" };
104545
- }
104546
- if (result.reason === "no_package_json") {
104547
- options.logger.warn(`No package.json found`);
104548
- return { updated: false, reason: "no_package_json" };
104549
- }
104550
- return { updated: false, reason: "already_present" };
104551
- }
104552
104633
  // src/lib/config/project.ts
104553
104634
  import { spawn } from "node:child_process";
104554
- import { existsSync as existsSync4 } from "node:fs";
104555
- import { resolve as resolve4 } from "node:path";
104556
- function isPackageInstalled(options) {
104557
- const { cwd, packageName } = options;
104558
- const parts = packageName.split("/");
104559
- return existsSync4(resolve4(cwd, "node_modules", ...parts, "package.json"));
104560
- }
104635
+ import { existsSync as existsSync3 } from "node:fs";
104636
+ import { resolve as resolve3 } from "node:path";
104561
104637
  function run(cmd, args, cwd, silent = false) {
104562
104638
  return new Promise((resolvePromise) => {
104563
104639
  const child = spawn(cmd, args, {
@@ -104569,24 +104645,11 @@ function run(cmd, args, cwd, silent = false) {
104569
104645
  child.on("error", () => resolvePromise(1));
104570
104646
  });
104571
104647
  }
104572
- async function runInstall(options) {
104573
- const { cwd, logger } = options;
104574
- const { packageManager } = detectPackageManager(cwd);
104575
- const { cmd, args } = getInstallCommand(packageManager);
104576
- logger.info(`Running ${cmd} ${args.join(" ")}...`);
104577
- const code = await run(cmd, args, cwd);
104578
- if (code === 0) {
104579
- logger.success("Dependencies installed");
104580
- return true;
104581
- }
104582
- logger.warn(`Install failed (exit ${code}). You may need to run install manually.`);
104583
- return false;
104584
- }
104585
104648
  async function formatWithPrettier(options) {
104586
104649
  const { cwd, filePath, logger, silent = false } = options;
104587
- const prettierBin = resolve4(cwd, "node_modules", ".bin", process.platform === "win32" ? "prettier.cmd" : "prettier");
104650
+ const prettierBin = resolve3(cwd, "node_modules", ".bin", process.platform === "win32" ? "prettier.cmd" : "prettier");
104588
104651
  const args = ["--write", filePath];
104589
- if (existsSync4(prettierBin)) {
104652
+ if (existsSync3(prettierBin)) {
104590
104653
  if (!silent)
104591
104654
  logger.info(`Formatting ${filePath} with prettier...`);
104592
104655
  const code2 = await run(prettierBin, args, cwd, silent);
@@ -104608,186 +104671,186 @@ async function formatWithPrettier(options) {
104608
104671
  return false;
104609
104672
  }
104610
104673
  // src/lib/config/generate.ts
104611
- function formatValue6(value) {
104612
- if (value === null)
104613
- return "null";
104614
- if (value === undefined)
104615
- return "undefined";
104616
- if (typeof value === "string")
104617
- return `'${value}'`;
104618
- if (typeof value === "number" || typeof value === "boolean")
104619
- return String(value);
104620
- if (Array.isArray(value)) {
104621
- return `[${value.map(formatValue6).join(", ")}]`;
104674
+ var SCHEMA_URL = "https://timeback.dev/schema.json";
104675
+ var TOP_LEVEL_KEY_ORDER = ["$schema", "name", "launchUrl", "sensor", "defaults", "courses"];
104676
+ var COURSE_KEY_ORDER = [
104677
+ "subject",
104678
+ "grade",
104679
+ "courseCode",
104680
+ "level",
104681
+ "sensor",
104682
+ "launchUrl",
104683
+ "ids",
104684
+ "metadata",
104685
+ "overrides"
104686
+ ];
104687
+ var IDS_KEY_ORDER = ["staging", "production"];
104688
+ var METADATA_KEY_ORDER = [
104689
+ "courseType",
104690
+ "isSupplemental",
104691
+ "isCustom",
104692
+ "publishStatus",
104693
+ "contactEmail",
104694
+ "primaryApp",
104695
+ "goals",
104696
+ "metrics"
104697
+ ];
104698
+ var DEFAULTS_KEY_ORDER = ["courseCode", "level", "metadata"];
104699
+ var OVERRIDES_KEY_ORDER = ["staging", "production"];
104700
+ var ENV_OVERRIDES_KEY_ORDER = ["level", "sensor", "launchUrl", "metadata"];
104701
+ function orderKeys(obj, keyOrder) {
104702
+ const result = {};
104703
+ const objKeys = Object.keys(obj);
104704
+ const source = obj;
104705
+ for (const key of keyOrder) {
104706
+ if (key in source && source[key] !== undefined) {
104707
+ result[key] = source[key];
104708
+ }
104622
104709
  }
104623
- if (typeof value === "object") {
104624
- const entries = Object.entries(value).filter(([, v2]) => v2 !== undefined);
104625
- if (entries.length === 0)
104626
- return "{}";
104627
- const inner = entries.map(([k3, v2]) => `${k3}: ${formatValue6(v2)}`).join(", ");
104628
- return `{ ${inner} }`;
104710
+ for (const key of objKeys) {
104711
+ if (!(key in result) && source[key] !== undefined) {
104712
+ result[key] = source[key];
104713
+ }
104629
104714
  }
104630
- return String(value);
104715
+ return result;
104716
+ }
104717
+ function filterUndefined(obj) {
104718
+ const result = {};
104719
+ for (const [key, value] of Object.entries(obj)) {
104720
+ if (value !== undefined) {
104721
+ result[key] = value;
104722
+ }
104723
+ }
104724
+ return result;
104631
104725
  }
104632
104726
  function formatCourseIds(ids) {
104633
104727
  if (!ids)
104634
- return "null";
104635
- const entries = Object.entries(ids).filter(([, v2]) => v2 !== undefined);
104636
- if (entries.length === 0)
104637
- return "null";
104638
- const inner = entries.map(([k3, v2]) => `${k3}: '${v2}'`).join(", ");
104639
- return `{ ${inner} }`;
104728
+ return;
104729
+ const filtered = filterUndefined(ids);
104730
+ if (Object.keys(filtered).length === 0)
104731
+ return;
104732
+ return orderKeys(filtered, IDS_KEY_ORDER);
104640
104733
  }
104641
- function formatMetadata(metadata, indent) {
104734
+ function formatMetadata(metadata) {
104642
104735
  if (!metadata)
104643
- return null;
104644
- const entries = Object.entries(metadata).filter(([, v2]) => v2 !== undefined);
104645
- if (entries.length === 0)
104646
- return null;
104647
- const parts = [];
104648
- if (metadata.courseType)
104649
- parts.push(`courseType: '${metadata.courseType}'`);
104650
- if (metadata.isSupplemental !== undefined)
104651
- parts.push(`isSupplemental: ${metadata.isSupplemental}`);
104652
- if (metadata.isCustom !== undefined)
104653
- parts.push(`isCustom: ${metadata.isCustom}`);
104654
- if (metadata.publishStatus)
104655
- parts.push(`publishStatus: '${metadata.publishStatus}'`);
104656
- if (metadata.contactEmail)
104657
- parts.push(`contactEmail: '${metadata.contactEmail}'`);
104658
- if (metadata.primaryApp)
104659
- parts.push(`primaryApp: '${metadata.primaryApp}'`);
104660
- if (metadata.goals)
104661
- parts.push(`goals: ${formatValue6(metadata.goals)}`);
104662
- if (metadata.metrics)
104663
- parts.push(`metrics: ${formatValue6(metadata.metrics)}`);
104664
- if (parts.length === 0)
104665
- return null;
104666
- return `{
104667
- ${indent} ${parts.join(`,
104668
- ${indent} `)},
104669
- ${indent}}`;
104736
+ return;
104737
+ const filtered = filterUndefined(metadata);
104738
+ if (Object.keys(filtered).length === 0)
104739
+ return;
104740
+ return orderKeys(filtered, METADATA_KEY_ORDER);
104670
104741
  }
104671
104742
  function formatDefaults(defaults) {
104672
104743
  if (!defaults)
104673
- return null;
104674
- const entries = Object.entries(defaults).filter(([, v2]) => v2 !== undefined);
104675
- if (entries.length === 0)
104676
- return null;
104677
- const parts = [];
104744
+ return;
104745
+ const result = {};
104678
104746
  if (defaults.courseCode)
104679
- parts.push(`courseCode: '${defaults.courseCode}'`);
104747
+ result.courseCode = defaults.courseCode;
104680
104748
  if (defaults.level)
104681
- parts.push(`level: '${defaults.level}'`);
104682
- const metadataStr = formatMetadata(defaults.metadata, "\t\t");
104683
- if (metadataStr)
104684
- parts.push(`metadata: ${metadataStr}`);
104685
- if (parts.length === 0)
104686
- return null;
104687
- return `{
104688
- ${parts.join(`,
104689
- `)},
104690
- }`;
104749
+ result.level = defaults.level;
104750
+ if (defaults.metadata) {
104751
+ const meta4 = formatMetadata(defaults.metadata);
104752
+ if (meta4)
104753
+ result.metadata = meta4;
104754
+ }
104755
+ if (Object.keys(result).length === 0)
104756
+ return;
104757
+ return orderKeys(result, DEFAULTS_KEY_ORDER);
104691
104758
  }
104692
- function formatEnvOverrides(overrides, indent) {
104759
+ function formatEnvOverrides(overrides) {
104693
104760
  if (!overrides)
104694
- return null;
104695
- const entries = Object.entries(overrides).filter(([, v2]) => v2 !== undefined);
104696
- if (entries.length === 0)
104697
- return null;
104698
- const parts = [];
104761
+ return;
104762
+ const result = {};
104699
104763
  if (overrides.level)
104700
- parts.push(`level: '${overrides.level}'`);
104764
+ result.level = overrides.level;
104701
104765
  if (overrides.sensor)
104702
- parts.push(`sensor: '${overrides.sensor}'`);
104766
+ result.sensor = overrides.sensor;
104703
104767
  if (overrides.launchUrl)
104704
- parts.push(`launchUrl: '${overrides.launchUrl}'`);
104705
- const metadataStr = formatMetadata(overrides.metadata, `${indent} `);
104706
- if (metadataStr)
104707
- parts.push(`metadata: ${metadataStr}`);
104708
- if (parts.length === 0)
104709
- return null;
104710
- return `{
104711
- ${indent} ${parts.join(`,
104712
- ${indent} `)},
104713
- ${indent}}`;
104768
+ result.launchUrl = overrides.launchUrl;
104769
+ if (overrides.metadata) {
104770
+ const meta4 = formatMetadata(overrides.metadata);
104771
+ if (meta4)
104772
+ result.metadata = meta4;
104773
+ }
104774
+ if (Object.keys(result).length === 0)
104775
+ return;
104776
+ return orderKeys(result, ENV_OVERRIDES_KEY_ORDER);
104714
104777
  }
104715
- function formatOverrides(overrides, indent) {
104778
+ function formatOverrides(overrides) {
104716
104779
  if (!overrides)
104717
- return null;
104718
- const parts = [];
104719
- const stagingStr = formatEnvOverrides(overrides.staging, `${indent} `);
104720
- const productionStr = formatEnvOverrides(overrides.production, `${indent} `);
104721
- if (stagingStr)
104722
- parts.push(`staging: ${stagingStr}`);
104723
- if (productionStr)
104724
- parts.push(`production: ${productionStr}`);
104725
- if (parts.length === 0)
104726
- return null;
104727
- return `{
104728
- ${indent} ${parts.join(`,
104729
- ${indent} `)},
104730
- ${indent}}`;
104780
+ return;
104781
+ const result = {};
104782
+ if (overrides.staging) {
104783
+ const staging = formatEnvOverrides(overrides.staging);
104784
+ if (staging)
104785
+ result.staging = staging;
104786
+ }
104787
+ if (overrides.production) {
104788
+ const production = formatEnvOverrides(overrides.production);
104789
+ if (production)
104790
+ result.production = production;
104791
+ }
104792
+ if (Object.keys(result).length === 0)
104793
+ return;
104794
+ return orderKeys(result, OVERRIDES_KEY_ORDER);
104731
104795
  }
104732
- function formatCourse(c) {
104733
- const parts = [`ids: ${formatCourseIds(c.ids)}`, `subject: '${c.subject}'`];
104734
- if (c.grade !== undefined) {
104735
- parts.push(`grade: ${c.grade}`);
104796
+ function formatCourse(course) {
104797
+ const result = {
104798
+ subject: course.subject
104799
+ };
104800
+ if (course.grade !== undefined) {
104801
+ result.grade = course.grade;
104802
+ }
104803
+ if (course.courseCode) {
104804
+ result.courseCode = course.courseCode;
104805
+ }
104806
+ if (course.level) {
104807
+ result.level = course.level;
104736
104808
  }
104737
- if (c.courseCode) {
104738
- parts.push(`courseCode: '${c.courseCode}'`);
104809
+ if (course.sensor) {
104810
+ result.sensor = course.sensor;
104739
104811
  }
104740
- if (c.level)
104741
- parts.push(`level: '${c.level}'`);
104742
- if (c.sensor) {
104743
- parts.push(`sensor: '${c.sensor}'`);
104812
+ if (course.launchUrl) {
104813
+ result.launchUrl = course.launchUrl;
104744
104814
  }
104745
- if (c.launchUrl) {
104746
- parts.push(`launchUrl: '${c.launchUrl}'`);
104815
+ const ids = formatCourseIds(course.ids);
104816
+ if (ids) {
104817
+ result.ids = ids;
104747
104818
  }
104748
- const metadataStr = formatMetadata(c.metadata, "\t\t\t");
104749
- if (metadataStr) {
104750
- parts.push(`metadata: ${metadataStr}`);
104819
+ const metadata = formatMetadata(course.metadata);
104820
+ if (metadata) {
104821
+ result.metadata = metadata;
104751
104822
  }
104752
- const overridesStr = formatOverrides(c.overrides, "\t\t\t");
104753
- if (overridesStr) {
104754
- parts.push(`overrides: ${overridesStr}`);
104823
+ const overrides = formatOverrides(course.overrides);
104824
+ if (overrides) {
104825
+ result.overrides = overrides;
104755
104826
  }
104756
- return `{
104757
- ${parts.join(`,
104758
- `)},
104759
- }`;
104827
+ return orderKeys(result, COURSE_KEY_ORDER);
104760
104828
  }
104761
104829
  function generateConfigContent(config7) {
104762
- const coursesStr = config7.courses.map(formatCourse).join(`,
104763
- `);
104764
- const defaultsStr = formatDefaults(config7.defaults);
104765
- const parts = [`name: '${config7.name}'`];
104830
+ const output2 = {
104831
+ $schema: SCHEMA_URL,
104832
+ name: config7.name
104833
+ };
104766
104834
  if (config7.launchUrl) {
104767
- parts.push(`launchUrl: '${config7.launchUrl}'`);
104835
+ output2.launchUrl = config7.launchUrl;
104768
104836
  }
104769
104837
  if (config7.sensor) {
104770
- parts.push(`sensor: '${config7.sensor}'`);
104838
+ output2.sensor = config7.sensor;
104771
104839
  }
104772
- if (defaultsStr) {
104773
- parts.push(`defaults: ${defaultsStr}`);
104840
+ const defaults = formatDefaults(config7.defaults);
104841
+ if (defaults) {
104842
+ output2.defaults = defaults;
104774
104843
  }
104775
- parts.push(`courses: [
104776
- ${coursesStr},
104777
- ]`);
104778
- return `import type { TimebackConfig } from '@timeback/sdk/config'
104779
-
104780
- export default {
104781
- ${parts.join(`,
104782
- `)},
104783
- } satisfies TimebackConfig
104844
+ output2.courses = config7.courses.map(formatCourse);
104845
+ const ordered = orderKeys(output2, TOP_LEVEL_KEY_ORDER);
104846
+ return JSON.stringify(ordered, null, 2) + `
104784
104847
  `;
104785
104848
  }
104786
104849
  // src/lib/config/loader.ts
104787
- import { writeFile as writeFile3 } from "node:fs/promises";
104850
+ import { writeFile as writeFile2 } from "node:fs/promises";
104788
104851
  async function saveConfig(configPath, config7) {
104789
104852
  const content = generateConfigContent(config7);
104790
- await writeFile3(configPath, content, "utf-8");
104853
+ await writeFile2(configPath, content, "utf-8");
104791
104854
  }
104792
104855
  // src/lib/compare.ts
104793
104856
  function compareValues(local, remote, path, changes, options = {}) {
@@ -105234,9 +105297,9 @@ async function searchAndSelectCourses2(options) {
105234
105297
  }
105235
105298
  }
105236
105299
  // src/lib/setup/setup.ts
105237
- import { writeFile as writeFile4 } from "node:fs/promises";
105238
- import { resolve as resolve5 } from "node:path";
105239
- var CONFIG_FILENAME = "timeback.config.ts";
105300
+ import { writeFile as writeFile3 } from "node:fs/promises";
105301
+ import { resolve as resolve4 } from "node:path";
105302
+ var CONFIG_FILENAME2 = "timeback.config.json";
105240
105303
  async function promptEnvironmentToSetup() {
105241
105304
  const env2 = await ve({
105242
105305
  message: "Which environment would you like to configure?",
@@ -105265,22 +105328,22 @@ You need to configure your Timeback API credentials first.`, "Credentials Setup"
105265
105328
  return true;
105266
105329
  }
105267
105330
  async function ensureConfig(opts = {}) {
105268
- const { configPath: customPath, skipIntro = false, install = true, format = true } = opts;
105331
+ const { configPath: customPath, skipIntro = false, format = true } = opts;
105269
105332
  if (!skipIntro) {
105270
105333
  intro("Timeback");
105271
105334
  }
105272
105335
  const cwd = process.cwd();
105273
- const configPath = customPath ? resolve5(cwd, customPath) : resolve5(cwd, CONFIG_FILENAME);
105336
+ const configPath = customPath ? resolve4(cwd, customPath) : resolve4(cwd, CONFIG_FILENAME2);
105274
105337
  const configResult = await loadConfig({ configPath: customPath });
105275
105338
  if (configResult.success) {
105276
- M2.info(`Found ${dim(CONFIG_FILENAME)}`);
105339
+ M2.info(`Found ${dim(CONFIG_FILENAME2)}`);
105277
105340
  return {
105278
105341
  success: true,
105279
105342
  config: configResult.config,
105280
105343
  configPath: configResult.configPath
105281
105344
  };
105282
105345
  }
105283
- Me(`No ${greenBright(CONFIG_FILENAME)} found in this directory.
105346
+ Me(`No ${greenBright(CONFIG_FILENAME2)} found in this directory.
105284
105347
  Let's set up your Timeback project.`, "First-time setup");
105285
105348
  let configuredEnvs = await getConfiguredEnvironments();
105286
105349
  if (configuredEnvs.length === 0) {
@@ -105311,12 +105374,8 @@ Let's set up your Timeback project.`, "First-time setup");
105311
105374
  courses: searchResult.courses
105312
105375
  };
105313
105376
  const content = generateConfigContent(config7);
105314
- await writeFile4(configPath, content, "utf-8");
105315
- M2.success(`Created ${dim(CONFIG_FILENAME)}`);
105316
- if (install) {
105317
- await ensureSdkDependency({ cwd, logger: M2 });
105318
- await runInstall({ cwd, logger: M2 });
105319
- }
105377
+ await writeFile3(configPath, content, "utf-8");
105378
+ M2.success(`Created ${dim(CONFIG_FILENAME2)}`);
105320
105379
  if (format) {
105321
105380
  await formatWithPrettier({ cwd, filePath: configPath, logger: M2 });
105322
105381
  }
@@ -105376,10 +105435,8 @@ async function runImportFlow(opts = {}) {
105376
105435
  }
105377
105436
  }
105378
105437
  const content = generateConfigContent(config7);
105379
- await writeFile4(configPath, content, "utf-8");
105380
- M2.success(`Updated ${dim(CONFIG_FILENAME)}`);
105381
- await ensureSdkDependency({ cwd: process.cwd(), logger: M2 });
105382
- await runInstall({ cwd: process.cwd(), logger: M2 });
105438
+ await writeFile3(configPath, content, "utf-8");
105439
+ M2.success(`Updated ${dim(CONFIG_FILENAME2)}`);
105383
105440
  if (format) {
105384
105441
  await formatWithPrettier({ cwd: process.cwd(), filePath: configPath, logger: M2 });
105385
105442
  }
@@ -109643,7 +109700,6 @@ async function importInteractive(options) {
109643
109700
  const createResult = await ensureConfig({
109644
109701
  configPath,
109645
109702
  skipIntro: true,
109646
- install: true,
109647
109703
  format
109648
109704
  });
109649
109705
  if (!createResult.success) {
@@ -109728,9 +109784,9 @@ function registerImportCommand(program2) {
109728
109784
  }
109729
109785
 
109730
109786
  // src/commands/init/create.ts
109731
- import { existsSync as existsSync5 } from "node:fs";
109732
- import { writeFile as writeFile5 } from "node:fs/promises";
109733
- import { basename as basename2, resolve as resolve6 } from "node:path";
109787
+ import { existsSync as existsSync4 } from "node:fs";
109788
+ import { writeFile as writeFile4 } from "node:fs/promises";
109789
+ import { basename as basename2, resolve as resolve5 } from "node:path";
109734
109790
 
109735
109791
  // src/commands/sync/lib/compare.ts
109736
109792
  function compareWithTitle(localTitle, remoteTitle, localConfig, remoteData) {
@@ -110016,7 +110072,7 @@ async function syncCommand(options) {
110016
110072
  }
110017
110073
 
110018
110074
  // src/commands/init/constants.ts
110019
- var DEFAULT_CONFIG_FILENAME = "timeback.config.ts";
110075
+ var DEFAULT_CONFIG_FILENAME = "timeback.config.json";
110020
110076
 
110021
110077
  // src/commands/init/lib/credentials.ts
110022
110078
  async function promptEnvironmentToSetup2() {
@@ -110323,17 +110379,16 @@ async function createConfig(options = {}) {
110323
110379
  const {
110324
110380
  configPath: customPath,
110325
110381
  exitOnComplete = true,
110326
- install = true,
110327
110382
  format = true,
110328
110383
  env: env2,
110329
110384
  yes = false,
110330
110385
  noSync = false
110331
110386
  } = options;
110332
110387
  const cwd = process.cwd();
110333
- const configPath = customPath ? resolve6(cwd, customPath) : resolve6(cwd, DEFAULT_CONFIG_FILENAME);
110388
+ const configPath = customPath ? resolve5(cwd, customPath) : resolve5(cwd, DEFAULT_CONFIG_FILENAME);
110334
110389
  const configFilename = basename2(configPath);
110335
110390
  intro("Timeback");
110336
- if (existsSync5(configPath)) {
110391
+ if (existsSync4(configPath)) {
110337
110392
  M2.warn(`${blueBright(configFilename)} already exists`);
110338
110393
  const overwrite = yes ? true : await ye({
110339
110394
  message: "Overwrite existing config?",
@@ -110368,20 +110423,11 @@ async function createConfig(options = {}) {
110368
110423
  const s = Y2();
110369
110424
  s.start(`Creating ${configFilename}...`);
110370
110425
  const content = generateConfigContent(config7);
110371
- await writeFile5(configPath, content, "utf-8");
110426
+ await writeFile4(configPath, content, "utf-8");
110372
110427
  if (format) {
110373
110428
  await formatWithPrettier({ cwd, filePath: configPath, logger: M2, silent: true });
110374
110429
  }
110375
110430
  s.stop(`Created ${dim(configFilename)}`);
110376
- const sdkDepResult = await ensureSdkDependency({ cwd, logger: M2 });
110377
- const sdkIsInstalled = isPackageInstalled({ cwd, packageName: "@timeback/sdk" });
110378
- const canInstall = install && sdkDepResult.reason !== "no_package_json";
110379
- const sdkWasAddedToPackageJson = sdkDepResult.updated;
110380
- const sdkMissingFromNodeModules = !sdkIsInstalled;
110381
- const installWouldHelp = sdkWasAddedToPackageJson || sdkMissingFromNodeModules;
110382
- if (canInstall && installWouldHelp) {
110383
- await runInstall({ cwd, logger: M2 });
110384
- }
110385
110431
  if (mode === "import") {
110386
110432
  outro.success();
110387
110433
  if (exitOnComplete)
@@ -110441,11 +110487,10 @@ async function createConfig(options = {}) {
110441
110487
 
110442
110488
  // src/commands/init/index.ts
110443
110489
  function registerInitCommand(program2) {
110444
- program2.command("init").description("Create a new timeback.config.ts file").option("-c, --config <path>", "Path to the config file (default: timeback.config.ts)").option("--env <environment>", "Target environment (staging, production)").option("-y, --yes", "Skip confirmations and use defaults where possible").option("--no-install", "Do not run install after updating package.json").option("--no-format", "Do not run prettier on the generated config file").option("--no-sync", "Skip syncing with Timeback after init").action((opts) => createConfig({
110490
+ program2.command("init").description("Create a new timeback.config.json file").option("-c, --config <path>", "Path to the config file (default: timeback.config.json)").option("--env <environment>", "Target environment (staging, production)").option("-y, --yes", "Skip confirmations and use defaults where possible").option("--no-format", "Do not run prettier on the generated config file").option("--no-sync", "Skip syncing with Timeback after init").action((opts) => createConfig({
110445
110491
  configPath: opts.config,
110446
110492
  env: opts.env,
110447
110493
  yes: opts.yes,
110448
- install: opts.install,
110449
110494
  format: opts.format,
110450
110495
  noSync: opts.sync === false
110451
110496
  }));
@@ -110858,13 +110903,12 @@ import O2 from "node:readline";
110858
110903
  import { Writable as X2 } from "node:stream";
110859
110904
  import y22 from "node:process";
110860
110905
  import * as tty2 from "tty";
110861
- import { mkdir as mkdir2, readFile as readFile4, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
110906
+ import { mkdir as mkdir2, readFile as readFile3, stat as stat2, writeFile as writeFile5 } from "node:fs/promises";
110862
110907
  import { homedir as homedir2, platform as platform22 } from "node:os";
110863
110908
  import { join as join2 } from "node:path";
110864
- import { existsSync as existsSync6 } from "node:fs";
110865
110909
  import { readFile as readFile22 } from "node:fs/promises";
110866
- import { basename as basename3, relative as relative2, resolve as resolve7 } from "node:path";
110867
- import { pathToFileURL as pathToFileURL2 } from "node:url";
110910
+ import { basename as basename3, extname as extname2, relative as relative2, resolve as resolve6 } from "node:path";
110911
+ import { loadConfig as c12LoadConfig2 } from "c12";
110868
110912
  import { basename as basename22 } from "node:path";
110869
110913
  import { createServer as createServerHTTP } from "http";
110870
110914
  import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
@@ -125444,7 +125488,7 @@ async function ensureCredentialsDir2() {
125444
125488
  }
125445
125489
  async function readCredentialsStore2() {
125446
125490
  try {
125447
- const content = await readFile4(CREDENTIALS_FILE2, "utf-8");
125491
+ const content = await readFile3(CREDENTIALS_FILE2, "utf-8");
125448
125492
  return JSON.parse(content);
125449
125493
  } catch {
125450
125494
  return {};
@@ -125453,7 +125497,7 @@ async function readCredentialsStore2() {
125453
125497
  async function writeCredentialsStore2(store) {
125454
125498
  if (!await ensureCredentialsDir2())
125455
125499
  return false;
125456
- await writeFile6(CREDENTIALS_FILE2, JSON.stringify(store, null, 2), { mode: 384 });
125500
+ await writeFile5(CREDENTIALS_FILE2, JSON.stringify(store, null, 2), { mode: 384 });
125457
125501
  return true;
125458
125502
  }
125459
125503
  async function getSavedCredentials2(environment) {
@@ -125536,7 +125580,7 @@ var outro2 = {
125536
125580
  warn: (message) => Se2(yellow2(message)),
125537
125581
  info: (message) => Se2(dim2(message))
125538
125582
  };
125539
- var FILE_PATTERNS2 = ["playcademy.config.ts", "playcademy.config.js", "playcademy.config.json"];
125583
+ var FILE_PATTERNS = ["playcademy.config.ts", "playcademy.config.js", "playcademy.config.json"];
125540
125584
  function parse52() {
125541
125585
  return Promise.resolve({
125542
125586
  success: false,
@@ -125559,7 +125603,7 @@ function printError(error483) {
125559
125603
  }
125560
125604
  var playcademyParser2 = {
125561
125605
  name: "playcademy",
125562
- filePatterns: FILE_PATTERNS2,
125606
+ filePatterns: FILE_PATTERNS,
125563
125607
  parse: parse52,
125564
125608
  printError
125565
125609
  };
@@ -125577,7 +125621,7 @@ var TimebackSubject8 = exports_external6.enum([
125577
125621
  "Math",
125578
125622
  "None",
125579
125623
  "Other"
125580
- ]);
125624
+ ]).meta({ id: "TimebackSubject", description: "Subject area" });
125581
125625
  var TimebackGrade8 = exports_external6.union([
125582
125626
  exports_external6.literal(-1),
125583
125627
  exports_external6.literal(0),
@@ -125594,7 +125638,10 @@ var TimebackGrade8 = exports_external6.union([
125594
125638
  exports_external6.literal(11),
125595
125639
  exports_external6.literal(12),
125596
125640
  exports_external6.literal(13)
125597
- ]);
125641
+ ]).meta({
125642
+ id: "TimebackGrade",
125643
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
125644
+ });
125598
125645
  var ScoreStatus8 = exports_external6.enum([
125599
125646
  "exempt",
125600
125647
  "fully graded",
@@ -125824,62 +125871,84 @@ var CaliperListEventsParams8 = exports_external6.object({
125824
125871
  actorEmail: exports_external6.email().optional()
125825
125872
  }).strict();
125826
125873
  var CourseIds8 = exports_external6.object({
125827
- staging: exports_external6.string().optional(),
125828
- production: exports_external6.string().optional()
125829
- });
125830
- var CourseType8 = exports_external6.enum(["base", "hole-filling", "optional"]);
125831
- var PublishStatus8 = exports_external6.enum(["draft", "testing", "published", "deactivated"]);
125874
+ staging: exports_external6.string().meta({ description: "Course ID in staging environment" }).optional(),
125875
+ production: exports_external6.string().meta({ description: "Course ID in production environment" }).optional()
125876
+ }).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
125877
+ var CourseType8 = exports_external6.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
125878
+ var PublishStatus8 = exports_external6.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
125832
125879
  var CourseGoals8 = exports_external6.object({
125833
- dailyXp: exports_external6.number().int().positive().optional(),
125834
- dailyLessons: exports_external6.number().int().positive().optional(),
125835
- dailyActiveMinutes: exports_external6.number().int().positive().optional(),
125836
- dailyAccuracy: exports_external6.number().int().min(0).max(100).optional(),
125837
- dailyMasteredUnits: exports_external6.number().int().positive().optional()
125838
- });
125880
+ dailyXp: exports_external6.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
125881
+ dailyLessons: exports_external6.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
125882
+ dailyActiveMinutes: exports_external6.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
125883
+ dailyAccuracy: exports_external6.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
125884
+ dailyMasteredUnits: exports_external6.number().int().positive().meta({ description: "Target units to master per day" }).optional()
125885
+ }).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
125839
125886
  var CourseMetrics8 = exports_external6.object({
125840
- totalXp: exports_external6.number().int().positive().optional(),
125841
- totalLessons: exports_external6.number().int().positive().optional(),
125842
- totalGrades: exports_external6.number().int().positive().optional()
125843
- });
125887
+ totalXp: exports_external6.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
125888
+ totalLessons: exports_external6.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
125889
+ totalGrades: exports_external6.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
125890
+ }).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
125844
125891
  var CourseMetadata8 = exports_external6.object({
125845
125892
  courseType: CourseType8.optional(),
125846
- isSupplemental: exports_external6.boolean().optional(),
125847
- isCustom: exports_external6.boolean().optional(),
125893
+ isSupplemental: exports_external6.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
125894
+ isCustom: exports_external6.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
125848
125895
  publishStatus: PublishStatus8.optional(),
125849
- contactEmail: exports_external6.email().optional(),
125850
- primaryApp: exports_external6.string().optional(),
125896
+ contactEmail: exports_external6.email().meta({ description: "Contact email for course issues" }).optional(),
125897
+ primaryApp: exports_external6.string().meta({ description: "Primary application identifier" }).optional(),
125851
125898
  goals: CourseGoals8.optional(),
125852
125899
  metrics: CourseMetrics8.optional()
125853
- });
125900
+ }).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
125854
125901
  var CourseDefaults8 = exports_external6.object({
125855
- courseCode: exports_external6.string().optional(),
125856
- level: exports_external6.string().optional(),
125902
+ courseCode: exports_external6.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
125903
+ level: exports_external6.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
125857
125904
  metadata: CourseMetadata8.optional()
125905
+ }).meta({
125906
+ id: "CourseDefaults",
125907
+ description: "Default properties that apply to all courses unless overridden"
125858
125908
  });
125859
125909
  var CourseEnvOverrides8 = exports_external6.object({
125860
- level: exports_external6.string().optional(),
125861
- sensor: exports_external6.string().url().optional(),
125862
- launchUrl: exports_external6.string().url().optional(),
125910
+ level: exports_external6.string().meta({ description: "Course level for this environment" }).optional(),
125911
+ sensor: exports_external6.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
125912
+ launchUrl: exports_external6.url().meta({ description: "LTI launch URL for this environment" }).optional(),
125863
125913
  metadata: CourseMetadata8.optional()
125914
+ }).meta({
125915
+ id: "CourseEnvOverrides",
125916
+ description: "Environment-specific course overrides (non-identity fields)"
125864
125917
  });
125865
125918
  var CourseOverrides8 = exports_external6.object({
125866
- staging: CourseEnvOverrides8.optional(),
125867
- production: CourseEnvOverrides8.optional()
125868
- });
125919
+ staging: CourseEnvOverrides8.meta({
125920
+ description: "Overrides for staging environment"
125921
+ }).optional(),
125922
+ production: CourseEnvOverrides8.meta({
125923
+ description: "Overrides for production environment"
125924
+ }).optional()
125925
+ }).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
125869
125926
  var CourseConfig8 = CourseDefaults8.extend({
125870
- subject: TimebackSubject8,
125871
- grade: TimebackGrade8.optional(),
125927
+ subject: TimebackSubject8.meta({ description: "Subject area for this course" }),
125928
+ grade: TimebackGrade8.meta({
125929
+ description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
125930
+ }).optional(),
125872
125931
  ids: CourseIds8.nullable().optional(),
125873
- sensor: exports_external6.string().url().optional(),
125874
- launchUrl: exports_external6.string().url().optional(),
125932
+ sensor: exports_external6.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
125933
+ launchUrl: exports_external6.url().meta({ description: "LTI launch URL for this course" }).optional(),
125875
125934
  overrides: CourseOverrides8.optional()
125935
+ }).meta({
125936
+ id: "CourseConfig",
125937
+ description: "Configuration for a single course. Must have either grade or courseCode (or both)."
125876
125938
  });
125877
125939
  var TimebackConfig8 = exports_external6.object({
125878
- name: exports_external6.string().min(1, "App name is required"),
125879
- defaults: CourseDefaults8.optional(),
125880
- courses: exports_external6.array(CourseConfig8).min(1, "At least one course is required"),
125881
- sensor: exports_external6.string().url().optional(),
125882
- launchUrl: exports_external6.string().url().optional()
125940
+ $schema: exports_external6.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
125941
+ name: exports_external6.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
125942
+ defaults: CourseDefaults8.meta({
125943
+ description: "Default properties applied to all courses"
125944
+ }).optional(),
125945
+ courses: exports_external6.array(CourseConfig8).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
125946
+ sensor: exports_external6.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
125947
+ launchUrl: exports_external6.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
125948
+ }).meta({
125949
+ id: "TimebackConfig",
125950
+ title: "Timeback Config",
125951
+ description: "Configuration schema for timeback.config.json files"
125883
125952
  }).refine((config23) => {
125884
125953
  return config23.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
125885
125954
  }, {
@@ -126889,26 +126958,10 @@ var QtiLessonFeedbackInput8 = exports_external6.object({
126889
126958
  lessonId: exports_external6.string().min(1),
126890
126959
  humanApproved: exports_external6.boolean().optional()
126891
126960
  }).strict();
126892
- var FILE_PATTERNS22 = ["timeback.config.ts", "timeback.config.js", "timeback.config.mjs"];
126893
- var isBun2 = typeof globalThis.Bun !== "undefined";
126894
- var jitiInstance2 = null;
126895
- async function getJiti2() {
126896
- if (jitiInstance2)
126897
- return jitiInstance2;
126898
- const { createJiti } = await import("jiti");
126899
- jitiInstance2 = createJiti(import.meta.url, { fsCache: false });
126900
- return jitiInstance2;
126901
- }
126902
- async function importModule2(fullPath) {
126903
- let module;
126904
- if (isBun2) {
126905
- const fileUrl = pathToFileURL2(fullPath).href;
126906
- module = await import(fileUrl);
126907
- } else {
126908
- const jiti = await getJiti2();
126909
- module = await jiti.import(fullPath);
126910
- }
126911
- return module.default ?? module;
126961
+ var CONFIG_FILENAME3 = "timeback.config.json";
126962
+ var FILE_PATTERNS2 = [CONFIG_FILENAME3];
126963
+ function isJsonConfigPath2(configPath) {
126964
+ return extname2(configPath).toLowerCase() === ".json";
126912
126965
  }
126913
126966
  function deriveCourseIds(config32) {
126914
126967
  const result = { staging: [], production: [] };
@@ -126919,65 +126972,80 @@ function deriveCourseIds(config32) {
126919
126972
  }
126920
126973
  async function readPackageVersion(cwd) {
126921
126974
  try {
126922
- const pkgPath = resolve7(cwd, "package.json");
126975
+ const pkgPath = resolve6(cwd, "package.json");
126923
126976
  const pkg = JSON.parse(await readFile22(pkgPath, "utf-8"));
126924
126977
  return pkg.version ?? "0.0.0";
126925
126978
  } catch {
126926
126979
  return "0.0.0";
126927
126980
  }
126928
126981
  }
126982
+ async function loadWithC122(cwd, configPath) {
126983
+ if (configPath && !isJsonConfigPath2(configPath)) {
126984
+ throw new Error(`Config file must be JSON (.json): ${configPath}`);
126985
+ }
126986
+ const result = await c12LoadConfig2({
126987
+ cwd,
126988
+ name: "timeback",
126989
+ configFile: configPath ?? CONFIG_FILENAME3,
126990
+ rcFile: false,
126991
+ packageJson: false,
126992
+ dotenv: false,
126993
+ envName: false,
126994
+ extend: false,
126995
+ omit$Keys: true,
126996
+ defaults: {},
126997
+ overrides: {}
126998
+ });
126999
+ if (!result.config || Object.keys(result.config).length === 0) {
127000
+ return null;
127001
+ }
127002
+ const rawConfig = result.config;
127003
+ if ("extends" in rawConfig) {
127004
+ throw new Error("The 'extends' feature is not supported in timeback.config.json. " + "Please inline all configuration.");
127005
+ }
127006
+ const { $schema: _schema, ...configWithoutSchema } = rawConfig;
127007
+ return {
127008
+ config: configWithoutSchema,
127009
+ configFile: result.configFile ?? resolve6(cwd, configPath ?? CONFIG_FILENAME3)
127010
+ };
127011
+ }
126929
127012
  async function parse62() {
126930
127013
  const cwd = process.cwd();
126931
- let rawConfig = null;
126932
- let foundPath = null;
126933
- let loadError = null;
126934
- for (const configPath of FILE_PATTERNS22) {
126935
- const fullPath = resolve7(cwd, configPath);
126936
- if (!existsSync6(fullPath))
126937
- continue;
126938
- try {
126939
- rawConfig = await importModule2(fullPath);
126940
- foundPath = configPath;
126941
- break;
126942
- } catch (err) {
126943
- loadError = err instanceof Error ? err : new Error(String(err));
126944
- foundPath = configPath;
126945
- break;
127014
+ try {
127015
+ const loaded = await loadWithC122(cwd);
127016
+ if (!loaded) {
127017
+ return {
127018
+ success: false,
127019
+ error: `No timeback config found. Create ${CONFIG_FILENAME3}`
127020
+ };
126946
127021
  }
126947
- }
126948
- if (loadError && foundPath) {
126949
- return {
126950
- success: false,
126951
- error: `Failed to load ${foundPath}:
126952
- ${loadError.message}`
126953
- };
126954
- }
126955
- if (!rawConfig || !foundPath) {
127022
+ const result = TimebackConfig8.safeParse(loaded.config);
127023
+ if (!result.success) {
127024
+ const issues = result.error.issues.map((issue26) => ` - ${issue26.path.join(".")}: ${issue26.message}`).join(`
127025
+ `);
127026
+ return {
127027
+ success: false,
127028
+ error: `Invalid config in ${CONFIG_FILENAME3}:
127029
+ ${issues}`
127030
+ };
127031
+ }
127032
+ const version23 = await readPackageVersion(cwd);
127033
+ const baseConfig = { ...result.data, version: version23 };
126956
127034
  return {
126957
- success: false,
126958
- error: `No timeback config found. Create one of: ${FILE_PATTERNS22.join(", ")}`
127035
+ success: true,
127036
+ config: {
127037
+ ...baseConfig,
127038
+ path: loaded.configFile,
127039
+ courseIds: deriveCourseIds(baseConfig)
127040
+ }
126959
127041
  };
126960
- }
126961
- const result = TimebackConfig8.safeParse(rawConfig);
126962
- if (!result.success) {
126963
- const issues = result.error.issues.map((issue26) => ` - ${issue26.path.join(".")}: ${issue26.message}`).join(`
126964
- `);
127042
+ } catch (err) {
126965
127043
  return {
126966
127044
  success: false,
126967
- error: `Invalid config in ${foundPath}:
126968
- ${issues}`
127045
+ error: `Failed to load ${CONFIG_FILENAME3}:
127046
+ ${err instanceof Error ? err.message : String(err)}`
126969
127047
  };
126970
127048
  }
126971
- const version23 = await readPackageVersion(cwd);
126972
- const baseConfig = { ...result.data, version: version23 };
126973
- return {
126974
- success: true,
126975
- config: {
126976
- ...baseConfig,
126977
- path: `${cwd}/${foundPath}`,
126978
- courseIds: deriveCourseIds(baseConfig)
126979
- }
126980
- };
126981
127049
  }
126982
127050
  function printError2(error483) {
126983
127051
  console.log();
@@ -126985,19 +127053,22 @@ function printError2(error483) {
126985
127053
  console.log();
126986
127054
  console.log(` ${error483}`);
126987
127055
  console.log();
126988
- console.log(` ${dim2("Example timeback.config.ts:")}`);
127056
+ console.log(` ${dim2("Example timeback.config.json:")}`);
126989
127057
  console.log();
126990
- console.log(` ${yellow2("export default {")}`);
126991
- console.log(` ${yellow2(" name: 'My Timeback App',")}`);
126992
- console.log(` ${yellow2(" courses: [")}`);
126993
- console.log(` ${yellow2(" { subject: 'Math', grade: 3 },")}`);
126994
- console.log(` ${yellow2(" ],")}`);
127058
+ console.log(` ${yellow2("{")}`);
127059
+ console.log(` ${yellow2(' "$schema": "https://timeback.dev/schema.json",')}`);
127060
+ console.log(` ${yellow2(' "name": "My Timeback App",')}`);
127061
+ console.log(` ${yellow2(' "launchUrl": "https://example.com/play",')}`);
127062
+ console.log(` ${yellow2(' "sensor": "https://example.com/sensor",')}`);
127063
+ console.log(` ${yellow2(' "courses": [')}`);
127064
+ console.log(` ${yellow2(' { "subject": "Math", "grade": 3 }')}`);
127065
+ console.log(` ${yellow2(" ]")}`);
126995
127066
  console.log(` ${yellow2("}")}`);
126996
127067
  console.log();
126997
127068
  }
126998
127069
  var timebackParser2 = {
126999
127070
  name: "timeback",
127000
- filePatterns: FILE_PATTERNS22,
127071
+ filePatterns: FILE_PATTERNS2,
127001
127072
  parse: parse62,
127002
127073
  printError: printError2
127003
127074
  };
@@ -127571,7 +127642,7 @@ async function listCredentials2(options = {}) {
127571
127642
  lines.push(`${green2("●")} ${env22}: ${creds.clientId}`);
127572
127643
  hasAny = true;
127573
127644
  } else {
127574
- lines.push(`${dim2("○")} ${env22}: not configured`);
127645
+ lines.push(`${dim2("○")} ${env22}: ${"not configured"}`);
127575
127646
  }
127576
127647
  }
127577
127648
  Me2(lines.join(`
@@ -130191,9 +130262,9 @@ var LEVEL_PREFIX9 = {
130191
130262
  error: "[ERROR]"
130192
130263
  };
130193
130264
  function formatContext26(context) {
130194
- return Object.entries(context).map(([key, value]) => `${key}=${formatValue10(value)}`).join(" ");
130265
+ return Object.entries(context).map(([key, value]) => `${key}=${formatValue6(value)}`).join(" ");
130195
130266
  }
130196
- function formatValue10(value) {
130267
+ function formatValue6(value) {
130197
130268
  if (typeof value === "string")
130198
130269
  return value;
130199
130270
  if (typeof value === "number")
@@ -131539,7 +131610,7 @@ function registerSyncCommand(program2) {
131539
131610
  }
131540
131611
 
131541
131612
  // src/cli.ts
131542
- var version8 = "0.1.5";
131613
+ var version8 = "0.1.6";
131543
131614
  program.name("timeback").description("CLI for Timeback tools and integrations").version(version8);
131544
131615
  registerApiCommand(program);
131545
131616
  registerCredentialsCommand(program);