@timeback/core 0.1.3 → 0.1.4
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/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/errors.js +30 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +645 -355
- package/dist/utils.js +119 -68
- package/package.json +7 -6
package/dist/utils.js
CHANGED
|
@@ -699,7 +699,17 @@ class TimebackProvider {
|
|
|
699
699
|
}
|
|
700
700
|
function getEnv(key) {
|
|
701
701
|
try {
|
|
702
|
-
|
|
702
|
+
if (typeof process === "undefined")
|
|
703
|
+
return;
|
|
704
|
+
if (typeof key === "string") {
|
|
705
|
+
return process.env[key];
|
|
706
|
+
}
|
|
707
|
+
for (const k of key) {
|
|
708
|
+
const value = process.env[k];
|
|
709
|
+
if (value !== undefined)
|
|
710
|
+
return value;
|
|
711
|
+
}
|
|
712
|
+
return;
|
|
703
713
|
} catch {
|
|
704
714
|
return;
|
|
705
715
|
}
|
|
@@ -730,6 +740,18 @@ var DEFAULT_PROVIDER_REGISTRY = {
|
|
|
730
740
|
}
|
|
731
741
|
}
|
|
732
742
|
};
|
|
743
|
+
function primaryEnvVar(key) {
|
|
744
|
+
if (typeof key === "string") {
|
|
745
|
+
return key;
|
|
746
|
+
}
|
|
747
|
+
if (key.length === 0) {
|
|
748
|
+
throw new Error(`Missing env var key: ${key}`);
|
|
749
|
+
}
|
|
750
|
+
return key[0];
|
|
751
|
+
}
|
|
752
|
+
function formatEnvVarKey(key) {
|
|
753
|
+
return primaryEnvVar(key);
|
|
754
|
+
}
|
|
733
755
|
function validateEnv(env) {
|
|
734
756
|
if (env !== "staging" && env !== "production") {
|
|
735
757
|
throw new Error(`Invalid env "${env}": must be "staging" or "production"`);
|
|
@@ -740,10 +762,10 @@ function validateAuth(auth, envVars) {
|
|
|
740
762
|
const clientId = auth?.clientId ?? getEnv(envVars.clientId);
|
|
741
763
|
const clientSecret = auth?.clientSecret ?? getEnv(envVars.clientSecret);
|
|
742
764
|
if (!clientId) {
|
|
743
|
-
throw new Error(`Missing clientId: provide in config or set ${envVars.clientId}`);
|
|
765
|
+
throw new Error(`Missing clientId: provide in config or set ${formatEnvVarKey(envVars.clientId)}`);
|
|
744
766
|
}
|
|
745
767
|
if (!clientSecret) {
|
|
746
|
-
throw new Error(`Missing clientSecret: provide in config or set ${envVars.clientSecret}`);
|
|
768
|
+
throw new Error(`Missing clientSecret: provide in config or set ${formatEnvVarKey(envVars.clientSecret)}`);
|
|
747
769
|
}
|
|
748
770
|
return { clientId, clientSecret };
|
|
749
771
|
}
|
|
@@ -759,21 +781,21 @@ function buildMissingEnvError(envVars) {
|
|
|
759
781
|
const clientId = getEnv(envVars.clientId);
|
|
760
782
|
const clientSecret = getEnv(envVars.clientSecret);
|
|
761
783
|
if (baseUrl === undefined && clientId === undefined) {
|
|
762
|
-
const hint = envVars.env ?? envVars.baseUrl;
|
|
784
|
+
const hint = formatEnvVarKey(envVars.env ?? envVars.baseUrl);
|
|
763
785
|
return `Missing env: provide in config or set ${hint}`;
|
|
764
786
|
}
|
|
765
787
|
const missing = [];
|
|
766
788
|
if (baseUrl === undefined) {
|
|
767
|
-
missing.push(envVars.env ?? envVars.baseUrl);
|
|
789
|
+
missing.push(formatEnvVarKey(envVars.env ?? envVars.baseUrl));
|
|
768
790
|
}
|
|
769
791
|
if (baseUrl !== undefined && authUrl === undefined) {
|
|
770
|
-
missing.push(envVars.authUrl);
|
|
792
|
+
missing.push(formatEnvVarKey(envVars.authUrl));
|
|
771
793
|
}
|
|
772
794
|
if (clientId === undefined) {
|
|
773
|
-
missing.push(envVars.clientId);
|
|
795
|
+
missing.push(formatEnvVarKey(envVars.clientId));
|
|
774
796
|
}
|
|
775
797
|
if (clientSecret === undefined) {
|
|
776
|
-
missing.push(envVars.clientSecret);
|
|
798
|
+
missing.push(formatEnvVarKey(envVars.clientSecret));
|
|
777
799
|
}
|
|
778
800
|
return `Missing environment variables: ${missing.join(", ")}`;
|
|
779
801
|
}
|
|
@@ -1264,10 +1286,10 @@ function validateNonEmptyString(value, name) {
|
|
|
1264
1286
|
}
|
|
1265
1287
|
}
|
|
1266
1288
|
var EDUBRIDGE_ENV_VARS = {
|
|
1267
|
-
baseUrl: "EDUBRIDGE_BASE_URL",
|
|
1268
|
-
clientId: "EDUBRIDGE_CLIENT_ID",
|
|
1269
|
-
clientSecret: "EDUBRIDGE_CLIENT_SECRET",
|
|
1270
|
-
authUrl: "EDUBRIDGE_TOKEN_URL"
|
|
1289
|
+
baseUrl: ["TIMEBACK_API_BASE_URL", "TIMEBACK_BASE_URL", "EDUBRIDGE_BASE_URL"],
|
|
1290
|
+
clientId: ["TIMEBACK_API_CLIENT_ID", "TIMEBACK_CLIENT_ID", "EDUBRIDGE_CLIENT_ID"],
|
|
1291
|
+
clientSecret: ["TIMEBACK_API_CLIENT_SECRET", "TIMEBACK_CLIENT_SECRET", "EDUBRIDGE_CLIENT_SECRET"],
|
|
1292
|
+
authUrl: ["TIMEBACK_API_AUTH_URL", "TIMEBACK_AUTH_URL", "EDUBRIDGE_TOKEN_URL"]
|
|
1271
1293
|
};
|
|
1272
1294
|
function resolveToProvider2(config, registry = DEFAULT_PROVIDER_REGISTRY) {
|
|
1273
1295
|
return resolveToProvider(config, EDUBRIDGE_ENV_VARS, registry);
|
|
@@ -3595,7 +3617,7 @@ class Doc {
|
|
|
3595
3617
|
var version = {
|
|
3596
3618
|
major: 4,
|
|
3597
3619
|
minor: 3,
|
|
3598
|
-
patch:
|
|
3620
|
+
patch: 6
|
|
3599
3621
|
};
|
|
3600
3622
|
var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
|
|
3601
3623
|
var _a;
|
|
@@ -4879,7 +4901,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
4879
4901
|
if (keyResult instanceof Promise) {
|
|
4880
4902
|
throw new Error("Async schemas not supported in object keys currently");
|
|
4881
4903
|
}
|
|
4882
|
-
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length
|
|
4904
|
+
const checkNumericKey = typeof key === "string" && number.test(key) && keyResult.issues.length;
|
|
4883
4905
|
if (checkNumericKey) {
|
|
4884
4906
|
const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);
|
|
4885
4907
|
if (retryResult instanceof Promise) {
|
|
@@ -12196,7 +12218,7 @@ function finalize(ctx, schema) {
|
|
|
12196
12218
|
}
|
|
12197
12219
|
}
|
|
12198
12220
|
}
|
|
12199
|
-
if (refSchema.$ref) {
|
|
12221
|
+
if (refSchema.$ref && refSeen.def) {
|
|
12200
12222
|
for (const key in schema2) {
|
|
12201
12223
|
if (key === "$ref" || key === "allOf")
|
|
12202
12224
|
continue;
|
|
@@ -14828,7 +14850,7 @@ var TimebackSubject = exports_external.enum([
|
|
|
14828
14850
|
"Math",
|
|
14829
14851
|
"None",
|
|
14830
14852
|
"Other"
|
|
14831
|
-
]);
|
|
14853
|
+
]).meta({ id: "TimebackSubject", description: "Subject area" });
|
|
14832
14854
|
var TimebackGrade = exports_external.union([
|
|
14833
14855
|
exports_external.literal(-1),
|
|
14834
14856
|
exports_external.literal(0),
|
|
@@ -14845,7 +14867,10 @@ var TimebackGrade = exports_external.union([
|
|
|
14845
14867
|
exports_external.literal(11),
|
|
14846
14868
|
exports_external.literal(12),
|
|
14847
14869
|
exports_external.literal(13)
|
|
14848
|
-
])
|
|
14870
|
+
]).meta({
|
|
14871
|
+
id: "TimebackGrade",
|
|
14872
|
+
description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
|
|
14873
|
+
});
|
|
14849
14874
|
var ScoreStatus = exports_external.enum([
|
|
14850
14875
|
"exempt",
|
|
14851
14876
|
"fully graded",
|
|
@@ -15075,62 +15100,84 @@ var CaliperListEventsParams = exports_external.object({
|
|
|
15075
15100
|
actorEmail: exports_external.email().optional()
|
|
15076
15101
|
}).strict();
|
|
15077
15102
|
var CourseIds = exports_external.object({
|
|
15078
|
-
staging: exports_external.string().optional(),
|
|
15079
|
-
production: exports_external.string().optional()
|
|
15080
|
-
});
|
|
15081
|
-
var CourseType = exports_external.enum(["base", "hole-filling", "optional"]);
|
|
15082
|
-
var PublishStatus = exports_external.enum(["draft", "testing", "published", "deactivated"]);
|
|
15103
|
+
staging: exports_external.string().meta({ description: "Course ID in staging environment" }).optional(),
|
|
15104
|
+
production: exports_external.string().meta({ description: "Course ID in production environment" }).optional()
|
|
15105
|
+
}).meta({ id: "CourseIds", description: "Environment-specific course IDs (populated by sync)" });
|
|
15106
|
+
var CourseType = exports_external.enum(["base", "hole-filling", "optional"]).meta({ id: "CourseType", description: "Course classification type" });
|
|
15107
|
+
var PublishStatus = exports_external.enum(["draft", "testing", "published", "deactivated"]).meta({ id: "PublishStatus", description: "Course publication status" });
|
|
15083
15108
|
var CourseGoals = exports_external.object({
|
|
15084
|
-
dailyXp: exports_external.number().int().positive().optional(),
|
|
15085
|
-
dailyLessons: exports_external.number().int().positive().optional(),
|
|
15086
|
-
dailyActiveMinutes: exports_external.number().int().positive().optional(),
|
|
15087
|
-
dailyAccuracy: exports_external.number().int().min(0).max(100).optional(),
|
|
15088
|
-
dailyMasteredUnits: exports_external.number().int().positive().optional()
|
|
15089
|
-
});
|
|
15109
|
+
dailyXp: exports_external.number().int().positive().meta({ description: "Target XP to earn per day" }).optional(),
|
|
15110
|
+
dailyLessons: exports_external.number().int().positive().meta({ description: "Target lessons to complete per day" }).optional(),
|
|
15111
|
+
dailyActiveMinutes: exports_external.number().int().positive().meta({ description: "Target active learning minutes per day" }).optional(),
|
|
15112
|
+
dailyAccuracy: exports_external.number().int().min(0).max(100).meta({ description: "Target accuracy percentage (0-100)" }).optional(),
|
|
15113
|
+
dailyMasteredUnits: exports_external.number().int().positive().meta({ description: "Target units to master per day" }).optional()
|
|
15114
|
+
}).meta({ id: "CourseGoals", description: "Daily learning goals for a course" });
|
|
15090
15115
|
var CourseMetrics = exports_external.object({
|
|
15091
|
-
totalXp: exports_external.number().int().positive().optional(),
|
|
15092
|
-
totalLessons: exports_external.number().int().positive().optional(),
|
|
15093
|
-
totalGrades: exports_external.number().int().positive().optional()
|
|
15094
|
-
});
|
|
15116
|
+
totalXp: exports_external.number().int().positive().meta({ description: "Total XP available in the course" }).optional(),
|
|
15117
|
+
totalLessons: exports_external.number().int().positive().meta({ description: "Total number of lessons/activities" }).optional(),
|
|
15118
|
+
totalGrades: exports_external.number().int().positive().meta({ description: "Total grade levels covered" }).optional()
|
|
15119
|
+
}).meta({ id: "CourseMetrics", description: "Aggregate metrics for a course" });
|
|
15095
15120
|
var CourseMetadata = exports_external.object({
|
|
15096
15121
|
courseType: CourseType.optional(),
|
|
15097
|
-
isSupplemental: exports_external.boolean().optional(),
|
|
15098
|
-
isCustom: exports_external.boolean().optional(),
|
|
15122
|
+
isSupplemental: exports_external.boolean().meta({ description: "Whether this is supplemental to a base course" }).optional(),
|
|
15123
|
+
isCustom: exports_external.boolean().meta({ description: "Whether this is a custom course for an individual student" }).optional(),
|
|
15099
15124
|
publishStatus: PublishStatus.optional(),
|
|
15100
|
-
contactEmail: exports_external.email().optional(),
|
|
15101
|
-
primaryApp: exports_external.string().optional(),
|
|
15125
|
+
contactEmail: exports_external.email().meta({ description: "Contact email for course issues" }).optional(),
|
|
15126
|
+
primaryApp: exports_external.string().meta({ description: "Primary application identifier" }).optional(),
|
|
15102
15127
|
goals: CourseGoals.optional(),
|
|
15103
15128
|
metrics: CourseMetrics.optional()
|
|
15104
|
-
});
|
|
15129
|
+
}).meta({ id: "CourseMetadata", description: "Course metadata (matches API metadata object)" });
|
|
15105
15130
|
var CourseDefaults = exports_external.object({
|
|
15106
|
-
courseCode: exports_external.string().optional(),
|
|
15107
|
-
level: exports_external.string().optional(),
|
|
15131
|
+
courseCode: exports_external.string().meta({ description: "Course code (e.g., 'MATH101')" }).optional(),
|
|
15132
|
+
level: exports_external.string().meta({ description: "Course level (e.g., 'AP', 'Honors')" }).optional(),
|
|
15108
15133
|
metadata: CourseMetadata.optional()
|
|
15134
|
+
}).meta({
|
|
15135
|
+
id: "CourseDefaults",
|
|
15136
|
+
description: "Default properties that apply to all courses unless overridden"
|
|
15109
15137
|
});
|
|
15110
15138
|
var CourseEnvOverrides = exports_external.object({
|
|
15111
|
-
level: exports_external.string().optional(),
|
|
15112
|
-
sensor: exports_external.
|
|
15113
|
-
launchUrl: exports_external.
|
|
15139
|
+
level: exports_external.string().meta({ description: "Course level for this environment" }).optional(),
|
|
15140
|
+
sensor: exports_external.url().meta({ description: "Caliper sensor endpoint URL for this environment" }).optional(),
|
|
15141
|
+
launchUrl: exports_external.url().meta({ description: "LTI launch URL for this environment" }).optional(),
|
|
15114
15142
|
metadata: CourseMetadata.optional()
|
|
15143
|
+
}).meta({
|
|
15144
|
+
id: "CourseEnvOverrides",
|
|
15145
|
+
description: "Environment-specific course overrides (non-identity fields)"
|
|
15115
15146
|
});
|
|
15116
15147
|
var CourseOverrides = exports_external.object({
|
|
15117
|
-
staging: CourseEnvOverrides.
|
|
15118
|
-
|
|
15119
|
-
})
|
|
15148
|
+
staging: CourseEnvOverrides.meta({
|
|
15149
|
+
description: "Overrides for staging environment"
|
|
15150
|
+
}).optional(),
|
|
15151
|
+
production: CourseEnvOverrides.meta({
|
|
15152
|
+
description: "Overrides for production environment"
|
|
15153
|
+
}).optional()
|
|
15154
|
+
}).meta({ id: "CourseOverrides", description: "Per-environment course overrides" });
|
|
15120
15155
|
var CourseConfig = CourseDefaults.extend({
|
|
15121
|
-
subject: TimebackSubject,
|
|
15122
|
-
grade: TimebackGrade.
|
|
15156
|
+
subject: TimebackSubject.meta({ description: "Subject area for this course" }),
|
|
15157
|
+
grade: TimebackGrade.meta({
|
|
15158
|
+
description: "Grade level (-1 = Pre-K, 0 = K, 1-12 = grades, 13 = AP)"
|
|
15159
|
+
}).optional(),
|
|
15123
15160
|
ids: CourseIds.nullable().optional(),
|
|
15124
|
-
sensor: exports_external.
|
|
15125
|
-
launchUrl: exports_external.
|
|
15161
|
+
sensor: exports_external.url().meta({ description: "Caliper sensor endpoint URL for this course" }).optional(),
|
|
15162
|
+
launchUrl: exports_external.url().meta({ description: "LTI launch URL for this course" }).optional(),
|
|
15126
15163
|
overrides: CourseOverrides.optional()
|
|
15164
|
+
}).meta({
|
|
15165
|
+
id: "CourseConfig",
|
|
15166
|
+
description: "Configuration for a single course. Must have either grade or courseCode (or both)."
|
|
15127
15167
|
});
|
|
15128
15168
|
var TimebackConfig = exports_external.object({
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15169
|
+
$schema: exports_external.string().meta({ description: "JSON Schema reference for editor support" }).optional(),
|
|
15170
|
+
name: exports_external.string().min(1, "App name is required").meta({ description: "Display name for your app" }),
|
|
15171
|
+
defaults: CourseDefaults.meta({
|
|
15172
|
+
description: "Default properties applied to all courses"
|
|
15173
|
+
}).optional(),
|
|
15174
|
+
courses: exports_external.array(CourseConfig).min(1, "At least one course is required").meta({ description: "Courses available in this app" }),
|
|
15175
|
+
sensor: exports_external.url().meta({ description: "Default Caliper sensor endpoint URL for all courses" }).optional(),
|
|
15176
|
+
launchUrl: exports_external.url().meta({ description: "Default LTI launch URL for all courses" }).optional()
|
|
15177
|
+
}).meta({
|
|
15178
|
+
id: "TimebackConfig",
|
|
15179
|
+
title: "Timeback Config",
|
|
15180
|
+
description: "Configuration schema for timeback.config.json files"
|
|
15134
15181
|
}).refine((config2) => {
|
|
15135
15182
|
return config2.courses.every((c) => c.grade !== undefined || c.courseCode !== undefined);
|
|
15136
15183
|
}, {
|
|
@@ -15151,17 +15198,23 @@ var TimebackConfig = exports_external.object({
|
|
|
15151
15198
|
message: "Duplicate courseCode found; each must be unique",
|
|
15152
15199
|
path: ["courses"]
|
|
15153
15200
|
}).refine((config2) => {
|
|
15154
|
-
return config2.courses.every((c) =>
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15201
|
+
return config2.courses.every((c) => {
|
|
15202
|
+
if (c.sensor !== undefined || config2.sensor !== undefined) {
|
|
15203
|
+
return true;
|
|
15204
|
+
}
|
|
15205
|
+
const launchUrls = [
|
|
15206
|
+
c.launchUrl,
|
|
15207
|
+
config2.launchUrl,
|
|
15208
|
+
c.overrides?.staging?.launchUrl,
|
|
15209
|
+
c.overrides?.production?.launchUrl
|
|
15210
|
+
].filter(Boolean);
|
|
15211
|
+
return launchUrls.length > 0;
|
|
15212
|
+
});
|
|
15160
15213
|
}, {
|
|
15161
|
-
message: "Each course must have an effective
|
|
15214
|
+
message: "Each course must have an effective sensor. Either set `sensor` explicitly (top-level or per-course), or provide a `launchUrl` so sensor can be derived from its origin.",
|
|
15162
15215
|
path: ["courses"]
|
|
15163
15216
|
});
|
|
15164
|
-
var EdubridgeDateString =
|
|
15217
|
+
var EdubridgeDateString = IsoDateTimeString;
|
|
15165
15218
|
var EduBridgeEnrollment = exports_external.object({
|
|
15166
15219
|
id: exports_external.string(),
|
|
15167
15220
|
role: exports_external.string(),
|
|
@@ -15225,13 +15278,11 @@ var EmailOrStudentId = exports_external.object({
|
|
|
15225
15278
|
});
|
|
15226
15279
|
var SubjectTrackInput = exports_external.object({
|
|
15227
15280
|
subject: NonEmptyString,
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
});
|
|
15232
|
-
var SubjectTrackUpsertInput = SubjectTrackInput.extend({
|
|
15233
|
-
id: NonEmptyString
|
|
15281
|
+
grade: NonEmptyString,
|
|
15282
|
+
courseId: NonEmptyString,
|
|
15283
|
+
orgSourcedId: NonEmptyString.optional()
|
|
15234
15284
|
});
|
|
15285
|
+
var SubjectTrackUpsertInput = SubjectTrackInput;
|
|
15235
15286
|
var EdubridgeListEnrollmentsParams = exports_external.object({
|
|
15236
15287
|
userId: NonEmptyString
|
|
15237
15288
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timeback/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -30,14 +30,15 @@
|
|
|
30
30
|
"test": "bun test --no-env-file unit.test.ts"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@timeback/caliper": "0.1.
|
|
34
|
-
"@timeback/edubridge": "0.1.
|
|
33
|
+
"@timeback/caliper": "0.1.3",
|
|
34
|
+
"@timeback/edubridge": "0.1.3",
|
|
35
35
|
"@timeback/internal-client-infra": "0.0.0",
|
|
36
36
|
"@timeback/internal-constants": "0.0.0",
|
|
37
|
+
"@timeback/internal-test": "0.0.0",
|
|
37
38
|
"@timeback/internal-utils": "0.0.0",
|
|
38
|
-
"@timeback/oneroster": "0.1.
|
|
39
|
-
"@timeback/powerpath": "0.1.
|
|
40
|
-
"@timeback/qti": "0.1.
|
|
39
|
+
"@timeback/oneroster": "0.1.5",
|
|
40
|
+
"@timeback/powerpath": "0.1.2",
|
|
41
|
+
"@timeback/qti": "0.1.2",
|
|
41
42
|
"@types/bun": "latest"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|