@playcademy/sdk 0.9.1-beta.3 → 0.10.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +78 -3
- package/dist/index.js +60 -3
- package/dist/internal.d.ts +79 -4
- package/dist/internal.js +60 -3
- package/dist/server/edge.d.ts +5 -0
- package/dist/server/edge.js +27 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +27 -0
- package/dist/types.d.ts +79 -4
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1145,9 +1145,7 @@ interface StartActivityResult {
|
|
|
1145
1145
|
|
|
1146
1146
|
/**
|
|
1147
1147
|
* A TimeBack enrollment for the current game session.
|
|
1148
|
-
* Alias for UserEnrollment without the optional gameId.
|
|
1149
|
-
* are available at `enrollment.enrollmentIds?.active` when supplied by the
|
|
1150
|
-
* platform.
|
|
1148
|
+
* Alias for UserEnrollment without the optional gameId.
|
|
1151
1149
|
*/
|
|
1152
1150
|
type TimebackEnrollment = Omit<UserEnrollment, 'gameId'>;
|
|
1153
1151
|
/**
|
|
@@ -1253,6 +1251,50 @@ interface TimebackUser extends TimebackUserContext {
|
|
|
1253
1251
|
* Call `xp.fetch()` to get XP from the server.
|
|
1254
1252
|
*/
|
|
1255
1253
|
xp: TimebackUserXp;
|
|
1254
|
+
/**
|
|
1255
|
+
* Mastery data for the current user.
|
|
1256
|
+
* Call `mastery.fetch()` to get mastery progress from the server.
|
|
1257
|
+
*/
|
|
1258
|
+
mastery: TimebackUserMastery;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Mastery data access for the current user.
|
|
1262
|
+
* Results are cached for 5 seconds to avoid redundant network requests.
|
|
1263
|
+
*/
|
|
1264
|
+
interface TimebackUserMastery {
|
|
1265
|
+
/**
|
|
1266
|
+
* Fetch mastery data from the server.
|
|
1267
|
+
* Returns mastery for all courses in this game, or filter by grade/subject.
|
|
1268
|
+
* Results are cached for 5 seconds (use `force: true` to bypass).
|
|
1269
|
+
*
|
|
1270
|
+
* @param options - Query options
|
|
1271
|
+
* @param options.grade - Grade level to filter (must be used with subject)
|
|
1272
|
+
* @param options.subject - Subject to filter (must be used with grade)
|
|
1273
|
+
* @param options.include - Additional data to include: 'perCourse'
|
|
1274
|
+
* @param options.force - Bypass cache and fetch fresh data (default: false)
|
|
1275
|
+
* @returns Promise resolving to mastery data
|
|
1276
|
+
*
|
|
1277
|
+
* @example
|
|
1278
|
+
* ```typescript
|
|
1279
|
+
* // Get total mastery for all game courses
|
|
1280
|
+
* const mastery = await client.timeback.user.mastery.fetch()
|
|
1281
|
+
*
|
|
1282
|
+
* // Get mastery for a specific grade/subject
|
|
1283
|
+
* const mastery = await client.timeback.user.mastery.fetch({
|
|
1284
|
+
* grade: 3,
|
|
1285
|
+
* subject: 'Math'
|
|
1286
|
+
* })
|
|
1287
|
+
*
|
|
1288
|
+
* // Get mastery with per-course breakdown
|
|
1289
|
+
* const mastery = await client.timeback.user.mastery.fetch({
|
|
1290
|
+
* include: ['perCourse']
|
|
1291
|
+
* })
|
|
1292
|
+
*
|
|
1293
|
+
* // Force fresh data
|
|
1294
|
+
* const mastery = await client.timeback.user.mastery.fetch({ force: true })
|
|
1295
|
+
* ```
|
|
1296
|
+
*/
|
|
1297
|
+
fetch(options?: GetMasteryOptions): Promise<MasteryResponse>;
|
|
1256
1298
|
}
|
|
1257
1299
|
/**
|
|
1258
1300
|
* Options for querying student XP.
|
|
@@ -1267,6 +1309,19 @@ interface GetXpOptions {
|
|
|
1267
1309
|
/** Bypass cache and fetch fresh data (default: false) */
|
|
1268
1310
|
force?: boolean;
|
|
1269
1311
|
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Options for querying student mastery.
|
|
1314
|
+
*/
|
|
1315
|
+
interface GetMasteryOptions {
|
|
1316
|
+
/** Grade level to filter (must be used with subject) */
|
|
1317
|
+
grade?: TimebackGrade;
|
|
1318
|
+
/** Subject to filter (must be used with grade) */
|
|
1319
|
+
subject?: TimebackSubject;
|
|
1320
|
+
/** Additional data to include: 'perCourse' */
|
|
1321
|
+
include?: 'perCourse'[];
|
|
1322
|
+
/** Bypass cache and fetch fresh data (default: false) */
|
|
1323
|
+
force?: boolean;
|
|
1324
|
+
}
|
|
1270
1325
|
/**
|
|
1271
1326
|
* XP data for a single course.
|
|
1272
1327
|
*/
|
|
@@ -1285,6 +1340,26 @@ interface XpResponse {
|
|
|
1285
1340
|
todayXp?: number;
|
|
1286
1341
|
courses?: CourseXp[];
|
|
1287
1342
|
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Mastery data for a single course.
|
|
1345
|
+
*/
|
|
1346
|
+
interface CourseMastery {
|
|
1347
|
+
grade: TimebackGrade;
|
|
1348
|
+
subject: TimebackSubject;
|
|
1349
|
+
title: string;
|
|
1350
|
+
masteredUnits: number;
|
|
1351
|
+
masterableUnits: number;
|
|
1352
|
+
pctComplete: number;
|
|
1353
|
+
isComplete: boolean;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Response from mastery query.
|
|
1357
|
+
*/
|
|
1358
|
+
interface MasteryResponse {
|
|
1359
|
+
totalMasteredUnits: number;
|
|
1360
|
+
totalMasterableUnits: number;
|
|
1361
|
+
courses?: CourseMastery[];
|
|
1362
|
+
}
|
|
1288
1363
|
|
|
1289
1364
|
/**
|
|
1290
1365
|
* Core client configuration and lifecycle types
|
package/dist/index.js
CHANGED
|
@@ -1145,6 +1145,7 @@ function isValidSubject(value) {
|
|
|
1145
1145
|
var TIMEBACK_ROUTES = {
|
|
1146
1146
|
END_ACTIVITY: "/integrations/timeback/end-activity",
|
|
1147
1147
|
GET_XP: "/integrations/timeback/xp",
|
|
1148
|
+
GET_MASTERY: "/integrations/timeback/mastery",
|
|
1148
1149
|
HEARTBEAT: "/integrations/timeback/heartbeat",
|
|
1149
1150
|
ADVANCE_COURSE: "/integrations/timeback/advance-course"
|
|
1150
1151
|
};
|
|
@@ -1705,6 +1706,9 @@ function createTimebackActivityTracker(client) {
|
|
|
1705
1706
|
const unreportedActiveMs = Math.max(0, activeTime - activity.totalPersistedActiveMs);
|
|
1706
1707
|
const unreportedPausedMs = Math.max(0, activity.pausedTime - activity.totalPersistedPausedMs);
|
|
1707
1708
|
const { correctQuestions, totalQuestions } = data;
|
|
1709
|
+
if (data.masteredUnits !== undefined && data.masteredUnitsAbsolute !== undefined) {
|
|
1710
|
+
throw new Error("Cannot provide both masteredUnits and masteredUnitsAbsolute — use one or the other");
|
|
1711
|
+
}
|
|
1708
1712
|
const request = {
|
|
1709
1713
|
runId: activity.runId,
|
|
1710
1714
|
resumeId: activity.resumeId,
|
|
@@ -1722,6 +1726,7 @@ function createTimebackActivityTracker(client) {
|
|
|
1722
1726
|
},
|
|
1723
1727
|
xpEarned: data.xpAwarded,
|
|
1724
1728
|
masteredUnits: data.masteredUnits,
|
|
1729
|
+
masteredUnitsAbsolute: data.masteredUnitsAbsolute,
|
|
1725
1730
|
extensions: data.extensions
|
|
1726
1731
|
};
|
|
1727
1732
|
try {
|
|
@@ -1780,6 +1785,10 @@ function createTimebackEngine(client) {
|
|
|
1780
1785
|
ttl: 5000,
|
|
1781
1786
|
keyPrefix: "game.timeback.xp"
|
|
1782
1787
|
});
|
|
1788
|
+
const masteryCache = createTTLCache({
|
|
1789
|
+
ttl: 5000,
|
|
1790
|
+
keyPrefix: "game.timeback.mastery"
|
|
1791
|
+
});
|
|
1783
1792
|
const enrollmentsCache = createTTLCache({
|
|
1784
1793
|
ttl: 5 * 60 * 1000,
|
|
1785
1794
|
keyPrefix: "game.timeback.enrollments"
|
|
@@ -1847,6 +1856,30 @@ function createTimebackEngine(client) {
|
|
|
1847
1856
|
return client["requestGameBackend"](endpoint, "GET");
|
|
1848
1857
|
}, { force: options.force });
|
|
1849
1858
|
}
|
|
1859
|
+
},
|
|
1860
|
+
mastery: {
|
|
1861
|
+
fetch: (options) => {
|
|
1862
|
+
const cacheKey = [
|
|
1863
|
+
options.grade ?? "",
|
|
1864
|
+
options.subject ?? "",
|
|
1865
|
+
options.include?.toSorted().join(",") ?? ""
|
|
1866
|
+
].join(":");
|
|
1867
|
+
return masteryCache.get(cacheKey, async () => {
|
|
1868
|
+
const params = new URLSearchParams;
|
|
1869
|
+
if (options.grade !== undefined) {
|
|
1870
|
+
params.set("grade", String(options.grade));
|
|
1871
|
+
}
|
|
1872
|
+
if (options.subject !== undefined) {
|
|
1873
|
+
params.set("subject", options.subject);
|
|
1874
|
+
}
|
|
1875
|
+
if (options.include?.length) {
|
|
1876
|
+
params.set("include", options.include.join(","));
|
|
1877
|
+
}
|
|
1878
|
+
const queryString = params.toString();
|
|
1879
|
+
const endpoint = `${TIMEBACK_ROUTES.GET_MASTERY}${queryString ? `?${queryString}` : ""}`;
|
|
1880
|
+
return client["requestGameBackend"](endpoint, "GET");
|
|
1881
|
+
}, { force: options.force });
|
|
1882
|
+
}
|
|
1850
1883
|
}
|
|
1851
1884
|
},
|
|
1852
1885
|
activity: {
|
|
@@ -1865,7 +1898,8 @@ function createTimebackEngine(client) {
|
|
|
1865
1898
|
}
|
|
1866
1899
|
|
|
1867
1900
|
// src/namespaces/game/timeback.ts
|
|
1868
|
-
var
|
|
1901
|
+
var VALID_XP_INCLUDE_OPTIONS = ["perCourse", "today"];
|
|
1902
|
+
var VALID_MASTERY_INCLUDE_OPTIONS = ["perCourse"];
|
|
1869
1903
|
function createTimebackNamespace(client) {
|
|
1870
1904
|
const engine = createTimebackEngine(client);
|
|
1871
1905
|
return {
|
|
@@ -1901,13 +1935,36 @@ function createTimebackNamespace(client) {
|
|
|
1901
1935
|
}
|
|
1902
1936
|
if (options.include?.length) {
|
|
1903
1937
|
for (const opt of options.include) {
|
|
1904
|
-
if (!
|
|
1905
|
-
throw new Error(`Invalid include option: ${opt}. Valid options: ${
|
|
1938
|
+
if (!VALID_XP_INCLUDE_OPTIONS.includes(opt)) {
|
|
1939
|
+
throw new Error(`Invalid include option: ${opt}. Valid options: ${VALID_XP_INCLUDE_OPTIONS.join(", ")}`);
|
|
1906
1940
|
}
|
|
1907
1941
|
}
|
|
1908
1942
|
}
|
|
1909
1943
|
return engine.user.xp.fetch(options);
|
|
1910
1944
|
}
|
|
1945
|
+
},
|
|
1946
|
+
mastery: {
|
|
1947
|
+
fetch: async (options = {}) => {
|
|
1948
|
+
const hasGrade = options.grade !== undefined;
|
|
1949
|
+
const hasSubject = options.subject !== undefined;
|
|
1950
|
+
if (hasGrade !== hasSubject) {
|
|
1951
|
+
throw new Error("Both grade and subject must be provided together");
|
|
1952
|
+
}
|
|
1953
|
+
if (hasGrade && !isValidGrade(options.grade)) {
|
|
1954
|
+
throw new Error(`Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`);
|
|
1955
|
+
}
|
|
1956
|
+
if (hasSubject && !isValidSubject(options.subject)) {
|
|
1957
|
+
throw new Error(`Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`);
|
|
1958
|
+
}
|
|
1959
|
+
if (options.include?.length) {
|
|
1960
|
+
for (const opt of options.include) {
|
|
1961
|
+
if (!VALID_MASTERY_INCLUDE_OPTIONS.includes(opt)) {
|
|
1962
|
+
throw new Error(`Invalid include option: ${opt}. Valid options: ${VALID_MASTERY_INCLUDE_OPTIONS.join(", ")}`);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return engine.user.mastery.fetch(options);
|
|
1967
|
+
}
|
|
1911
1968
|
}
|
|
1912
1969
|
};
|
|
1913
1970
|
},
|
package/dist/internal.d.ts
CHANGED
|
@@ -999,9 +999,7 @@ interface StartActivityResult {
|
|
|
999
999
|
|
|
1000
1000
|
/**
|
|
1001
1001
|
* A TimeBack enrollment for the current game session.
|
|
1002
|
-
* Alias for UserEnrollment without the optional gameId.
|
|
1003
|
-
* are available at `enrollment.enrollmentIds?.active` when supplied by the
|
|
1004
|
-
* platform.
|
|
1002
|
+
* Alias for UserEnrollment without the optional gameId.
|
|
1005
1003
|
*/
|
|
1006
1004
|
type TimebackEnrollment = Omit<UserEnrollment, 'gameId'>;
|
|
1007
1005
|
/**
|
|
@@ -1107,6 +1105,50 @@ interface TimebackUser extends TimebackUserContext {
|
|
|
1107
1105
|
* Call `xp.fetch()` to get XP from the server.
|
|
1108
1106
|
*/
|
|
1109
1107
|
xp: TimebackUserXp;
|
|
1108
|
+
/**
|
|
1109
|
+
* Mastery data for the current user.
|
|
1110
|
+
* Call `mastery.fetch()` to get mastery progress from the server.
|
|
1111
|
+
*/
|
|
1112
|
+
mastery: TimebackUserMastery;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Mastery data access for the current user.
|
|
1116
|
+
* Results are cached for 5 seconds to avoid redundant network requests.
|
|
1117
|
+
*/
|
|
1118
|
+
interface TimebackUserMastery {
|
|
1119
|
+
/**
|
|
1120
|
+
* Fetch mastery data from the server.
|
|
1121
|
+
* Returns mastery for all courses in this game, or filter by grade/subject.
|
|
1122
|
+
* Results are cached for 5 seconds (use `force: true` to bypass).
|
|
1123
|
+
*
|
|
1124
|
+
* @param options - Query options
|
|
1125
|
+
* @param options.grade - Grade level to filter (must be used with subject)
|
|
1126
|
+
* @param options.subject - Subject to filter (must be used with grade)
|
|
1127
|
+
* @param options.include - Additional data to include: 'perCourse'
|
|
1128
|
+
* @param options.force - Bypass cache and fetch fresh data (default: false)
|
|
1129
|
+
* @returns Promise resolving to mastery data
|
|
1130
|
+
*
|
|
1131
|
+
* @example
|
|
1132
|
+
* ```typescript
|
|
1133
|
+
* // Get total mastery for all game courses
|
|
1134
|
+
* const mastery = await client.timeback.user.mastery.fetch()
|
|
1135
|
+
*
|
|
1136
|
+
* // Get mastery for a specific grade/subject
|
|
1137
|
+
* const mastery = await client.timeback.user.mastery.fetch({
|
|
1138
|
+
* grade: 3,
|
|
1139
|
+
* subject: 'Math'
|
|
1140
|
+
* })
|
|
1141
|
+
*
|
|
1142
|
+
* // Get mastery with per-course breakdown
|
|
1143
|
+
* const mastery = await client.timeback.user.mastery.fetch({
|
|
1144
|
+
* include: ['perCourse']
|
|
1145
|
+
* })
|
|
1146
|
+
*
|
|
1147
|
+
* // Force fresh data
|
|
1148
|
+
* const mastery = await client.timeback.user.mastery.fetch({ force: true })
|
|
1149
|
+
* ```
|
|
1150
|
+
*/
|
|
1151
|
+
fetch(options?: GetMasteryOptions): Promise<MasteryResponse>;
|
|
1110
1152
|
}
|
|
1111
1153
|
/**
|
|
1112
1154
|
* Options for querying student XP.
|
|
@@ -1121,6 +1163,19 @@ interface GetXpOptions {
|
|
|
1121
1163
|
/** Bypass cache and fetch fresh data (default: false) */
|
|
1122
1164
|
force?: boolean;
|
|
1123
1165
|
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Options for querying student mastery.
|
|
1168
|
+
*/
|
|
1169
|
+
interface GetMasteryOptions {
|
|
1170
|
+
/** Grade level to filter (must be used with subject) */
|
|
1171
|
+
grade?: TimebackGrade;
|
|
1172
|
+
/** Subject to filter (must be used with grade) */
|
|
1173
|
+
subject?: TimebackSubject;
|
|
1174
|
+
/** Additional data to include: 'perCourse' */
|
|
1175
|
+
include?: 'perCourse'[];
|
|
1176
|
+
/** Bypass cache and fetch fresh data (default: false) */
|
|
1177
|
+
force?: boolean;
|
|
1178
|
+
}
|
|
1124
1179
|
/**
|
|
1125
1180
|
* XP data for a single course.
|
|
1126
1181
|
*/
|
|
@@ -1139,6 +1194,26 @@ interface XpResponse {
|
|
|
1139
1194
|
todayXp?: number;
|
|
1140
1195
|
courses?: CourseXp[];
|
|
1141
1196
|
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Mastery data for a single course.
|
|
1199
|
+
*/
|
|
1200
|
+
interface CourseMastery {
|
|
1201
|
+
grade: TimebackGrade;
|
|
1202
|
+
subject: TimebackSubject;
|
|
1203
|
+
title: string;
|
|
1204
|
+
masteredUnits: number;
|
|
1205
|
+
masterableUnits: number;
|
|
1206
|
+
pctComplete: number;
|
|
1207
|
+
isComplete: boolean;
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Response from mastery query.
|
|
1211
|
+
*/
|
|
1212
|
+
interface MasteryResponse {
|
|
1213
|
+
totalMasteredUnits: number;
|
|
1214
|
+
totalMasterableUnits: number;
|
|
1215
|
+
courses?: CourseMastery[];
|
|
1216
|
+
}
|
|
1142
1217
|
|
|
1143
1218
|
/**
|
|
1144
1219
|
* Core client configuration and lifecycle types
|
|
@@ -3025,4 +3100,4 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3025
3100
|
}
|
|
3026
3101
|
|
|
3027
3102
|
export { ApiError, MessageEvents, PlaycademyInternalClient as PlaycademyClient, PlaycademyError, PlaycademyInternalClient, extractApiErrorInfo, messaging };
|
|
3028
|
-
export type { ApiErrorCode, ApiErrorInfo, AssessmentBankStatus, AssessmentRow, AssessmentSummary, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, ClientConfig, ClientEvents, CourseXp, DemoEndOptions, DemoEndPayload, DevUploadEvent, DevUploadHooks, ErrorResponseBody, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameRow as GameRecord, GameTokenResponse, GetXpOptions, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LoginResponse, MessageEventMap, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, ScoreSubmission, StartActivityOptions, StartActivityResult, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, XpResponse };
|
|
3103
|
+
export type { ApiErrorCode, ApiErrorInfo, AssessmentBankStatus, AssessmentRow, AssessmentSummary, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, ClientConfig, ClientEvents, CourseMastery, CourseXp, DemoEndOptions, DemoEndPayload, DevUploadEvent, DevUploadHooks, ErrorResponseBody, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameRow as GameRecord, GameTokenResponse, GetMasteryOptions, GetXpOptions, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LoginResponse, MasteryResponse, MessageEventMap, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, ScoreSubmission, StartActivityOptions, StartActivityResult, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserMastery, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, XpResponse };
|
package/dist/internal.js
CHANGED
|
@@ -1145,6 +1145,7 @@ function isValidSubject(value) {
|
|
|
1145
1145
|
var TIMEBACK_ROUTES = {
|
|
1146
1146
|
END_ACTIVITY: "/integrations/timeback/end-activity",
|
|
1147
1147
|
GET_XP: "/integrations/timeback/xp",
|
|
1148
|
+
GET_MASTERY: "/integrations/timeback/mastery",
|
|
1148
1149
|
HEARTBEAT: "/integrations/timeback/heartbeat",
|
|
1149
1150
|
ADVANCE_COURSE: "/integrations/timeback/advance-course"
|
|
1150
1151
|
};
|
|
@@ -1705,6 +1706,9 @@ function createTimebackActivityTracker(client) {
|
|
|
1705
1706
|
const unreportedActiveMs = Math.max(0, activeTime - activity.totalPersistedActiveMs);
|
|
1706
1707
|
const unreportedPausedMs = Math.max(0, activity.pausedTime - activity.totalPersistedPausedMs);
|
|
1707
1708
|
const { correctQuestions, totalQuestions } = data;
|
|
1709
|
+
if (data.masteredUnits !== undefined && data.masteredUnitsAbsolute !== undefined) {
|
|
1710
|
+
throw new Error("Cannot provide both masteredUnits and masteredUnitsAbsolute — use one or the other");
|
|
1711
|
+
}
|
|
1708
1712
|
const request = {
|
|
1709
1713
|
runId: activity.runId,
|
|
1710
1714
|
resumeId: activity.resumeId,
|
|
@@ -1722,6 +1726,7 @@ function createTimebackActivityTracker(client) {
|
|
|
1722
1726
|
},
|
|
1723
1727
|
xpEarned: data.xpAwarded,
|
|
1724
1728
|
masteredUnits: data.masteredUnits,
|
|
1729
|
+
masteredUnitsAbsolute: data.masteredUnitsAbsolute,
|
|
1725
1730
|
extensions: data.extensions
|
|
1726
1731
|
};
|
|
1727
1732
|
try {
|
|
@@ -1780,6 +1785,10 @@ function createTimebackEngine(client) {
|
|
|
1780
1785
|
ttl: 5000,
|
|
1781
1786
|
keyPrefix: "game.timeback.xp"
|
|
1782
1787
|
});
|
|
1788
|
+
const masteryCache = createTTLCache({
|
|
1789
|
+
ttl: 5000,
|
|
1790
|
+
keyPrefix: "game.timeback.mastery"
|
|
1791
|
+
});
|
|
1783
1792
|
const enrollmentsCache = createTTLCache({
|
|
1784
1793
|
ttl: 5 * 60 * 1000,
|
|
1785
1794
|
keyPrefix: "game.timeback.enrollments"
|
|
@@ -1847,6 +1856,30 @@ function createTimebackEngine(client) {
|
|
|
1847
1856
|
return client["requestGameBackend"](endpoint, "GET");
|
|
1848
1857
|
}, { force: options.force });
|
|
1849
1858
|
}
|
|
1859
|
+
},
|
|
1860
|
+
mastery: {
|
|
1861
|
+
fetch: (options) => {
|
|
1862
|
+
const cacheKey = [
|
|
1863
|
+
options.grade ?? "",
|
|
1864
|
+
options.subject ?? "",
|
|
1865
|
+
options.include?.toSorted().join(",") ?? ""
|
|
1866
|
+
].join(":");
|
|
1867
|
+
return masteryCache.get(cacheKey, async () => {
|
|
1868
|
+
const params = new URLSearchParams;
|
|
1869
|
+
if (options.grade !== undefined) {
|
|
1870
|
+
params.set("grade", String(options.grade));
|
|
1871
|
+
}
|
|
1872
|
+
if (options.subject !== undefined) {
|
|
1873
|
+
params.set("subject", options.subject);
|
|
1874
|
+
}
|
|
1875
|
+
if (options.include?.length) {
|
|
1876
|
+
params.set("include", options.include.join(","));
|
|
1877
|
+
}
|
|
1878
|
+
const queryString = params.toString();
|
|
1879
|
+
const endpoint = `${TIMEBACK_ROUTES.GET_MASTERY}${queryString ? `?${queryString}` : ""}`;
|
|
1880
|
+
return client["requestGameBackend"](endpoint, "GET");
|
|
1881
|
+
}, { force: options.force });
|
|
1882
|
+
}
|
|
1850
1883
|
}
|
|
1851
1884
|
},
|
|
1852
1885
|
activity: {
|
|
@@ -1865,7 +1898,8 @@ function createTimebackEngine(client) {
|
|
|
1865
1898
|
}
|
|
1866
1899
|
|
|
1867
1900
|
// src/namespaces/game/timeback.ts
|
|
1868
|
-
var
|
|
1901
|
+
var VALID_XP_INCLUDE_OPTIONS = ["perCourse", "today"];
|
|
1902
|
+
var VALID_MASTERY_INCLUDE_OPTIONS = ["perCourse"];
|
|
1869
1903
|
function createTimebackNamespace(client) {
|
|
1870
1904
|
const engine = createTimebackEngine(client);
|
|
1871
1905
|
return {
|
|
@@ -1901,13 +1935,36 @@ function createTimebackNamespace(client) {
|
|
|
1901
1935
|
}
|
|
1902
1936
|
if (options.include?.length) {
|
|
1903
1937
|
for (const opt of options.include) {
|
|
1904
|
-
if (!
|
|
1905
|
-
throw new Error(`Invalid include option: ${opt}. Valid options: ${
|
|
1938
|
+
if (!VALID_XP_INCLUDE_OPTIONS.includes(opt)) {
|
|
1939
|
+
throw new Error(`Invalid include option: ${opt}. Valid options: ${VALID_XP_INCLUDE_OPTIONS.join(", ")}`);
|
|
1906
1940
|
}
|
|
1907
1941
|
}
|
|
1908
1942
|
}
|
|
1909
1943
|
return engine.user.xp.fetch(options);
|
|
1910
1944
|
}
|
|
1945
|
+
},
|
|
1946
|
+
mastery: {
|
|
1947
|
+
fetch: async (options = {}) => {
|
|
1948
|
+
const hasGrade = options.grade !== undefined;
|
|
1949
|
+
const hasSubject = options.subject !== undefined;
|
|
1950
|
+
if (hasGrade !== hasSubject) {
|
|
1951
|
+
throw new Error("Both grade and subject must be provided together");
|
|
1952
|
+
}
|
|
1953
|
+
if (hasGrade && !isValidGrade(options.grade)) {
|
|
1954
|
+
throw new Error(`Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`);
|
|
1955
|
+
}
|
|
1956
|
+
if (hasSubject && !isValidSubject(options.subject)) {
|
|
1957
|
+
throw new Error(`Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`);
|
|
1958
|
+
}
|
|
1959
|
+
if (options.include?.length) {
|
|
1960
|
+
for (const opt of options.include) {
|
|
1961
|
+
if (!VALID_MASTERY_INCLUDE_OPTIONS.includes(opt)) {
|
|
1962
|
+
throw new Error(`Invalid include option: ${opt}. Valid options: ${VALID_MASTERY_INCLUDE_OPTIONS.join(", ")}`);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return engine.user.mastery.fetch(options);
|
|
1967
|
+
}
|
|
1911
1968
|
}
|
|
1912
1969
|
};
|
|
1913
1970
|
},
|
package/dist/server/edge.d.ts
CHANGED
|
@@ -298,6 +298,11 @@ declare class PlaycademyClient {
|
|
|
298
298
|
subject?: string;
|
|
299
299
|
include?: ('perCourse' | 'today')[];
|
|
300
300
|
}) => Promise<_playcademy_types.StudentXpResponse>;
|
|
301
|
+
getStudentMastery: (studentId: string, options?: {
|
|
302
|
+
grade?: number;
|
|
303
|
+
subject?: string;
|
|
304
|
+
include?: 'perCourse'[];
|
|
305
|
+
}) => Promise<_playcademy_types.StudentMasteryResponse>;
|
|
301
306
|
};
|
|
302
307
|
}
|
|
303
308
|
|
package/dist/server/edge.js
CHANGED
|
@@ -83,6 +83,33 @@ function createTimebackNamespace(client) {
|
|
|
83
83
|
const queryString = params.toString();
|
|
84
84
|
const endpoint = `/api/timeback/student-xp/${studentId}?${queryString}`;
|
|
85
85
|
return client["request"](endpoint, "GET");
|
|
86
|
+
},
|
|
87
|
+
getStudentMastery: async (studentId, options) => {
|
|
88
|
+
const hasGrade = options?.grade !== undefined;
|
|
89
|
+
const hasSubject = options?.subject !== undefined;
|
|
90
|
+
if (hasGrade !== hasSubject) {
|
|
91
|
+
throw new Error("Both grade and subject must be provided together");
|
|
92
|
+
}
|
|
93
|
+
if (hasGrade && !isValidGrade(options.grade)) {
|
|
94
|
+
throw new Error(`Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`);
|
|
95
|
+
}
|
|
96
|
+
if (hasSubject && !isValidSubject(options.subject)) {
|
|
97
|
+
throw new Error(`Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`);
|
|
98
|
+
}
|
|
99
|
+
const params = new URLSearchParams;
|
|
100
|
+
params.set("gameId", client.gameId);
|
|
101
|
+
if (options?.grade !== undefined) {
|
|
102
|
+
params.set("grade", String(options.grade));
|
|
103
|
+
}
|
|
104
|
+
if (options?.subject) {
|
|
105
|
+
params.set("subject", options.subject);
|
|
106
|
+
}
|
|
107
|
+
if (options?.include?.length) {
|
|
108
|
+
params.set("include", options.include.join(","));
|
|
109
|
+
}
|
|
110
|
+
const queryString = params.toString();
|
|
111
|
+
const endpoint = `/api/timeback/student-mastery/${studentId}?${queryString}`;
|
|
112
|
+
return client["request"](endpoint, "GET");
|
|
86
113
|
}
|
|
87
114
|
};
|
|
88
115
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -298,6 +298,11 @@ declare class PlaycademyClient$1 {
|
|
|
298
298
|
subject?: string;
|
|
299
299
|
include?: ('perCourse' | 'today')[];
|
|
300
300
|
}) => Promise<_playcademy_types.StudentXpResponse>;
|
|
301
|
+
getStudentMastery: (studentId: string, options?: {
|
|
302
|
+
grade?: number;
|
|
303
|
+
subject?: string;
|
|
304
|
+
include?: 'perCourse'[];
|
|
305
|
+
}) => Promise<_playcademy_types.StudentMasteryResponse>;
|
|
301
306
|
};
|
|
302
307
|
}
|
|
303
308
|
|
package/dist/server.js
CHANGED
|
@@ -272,6 +272,33 @@ function createTimebackNamespace(client) {
|
|
|
272
272
|
const queryString = params.toString();
|
|
273
273
|
const endpoint = `/api/timeback/student-xp/${studentId}?${queryString}`;
|
|
274
274
|
return client["request"](endpoint, "GET");
|
|
275
|
+
},
|
|
276
|
+
getStudentMastery: async (studentId, options) => {
|
|
277
|
+
const hasGrade = options?.grade !== undefined;
|
|
278
|
+
const hasSubject = options?.subject !== undefined;
|
|
279
|
+
if (hasGrade !== hasSubject) {
|
|
280
|
+
throw new Error("Both grade and subject must be provided together");
|
|
281
|
+
}
|
|
282
|
+
if (hasGrade && !isValidGrade(options.grade)) {
|
|
283
|
+
throw new Error(`Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`);
|
|
284
|
+
}
|
|
285
|
+
if (hasSubject && !isValidSubject(options.subject)) {
|
|
286
|
+
throw new Error(`Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`);
|
|
287
|
+
}
|
|
288
|
+
const params = new URLSearchParams;
|
|
289
|
+
params.set("gameId", client.gameId);
|
|
290
|
+
if (options?.grade !== undefined) {
|
|
291
|
+
params.set("grade", String(options.grade));
|
|
292
|
+
}
|
|
293
|
+
if (options?.subject) {
|
|
294
|
+
params.set("subject", options.subject);
|
|
295
|
+
}
|
|
296
|
+
if (options?.include?.length) {
|
|
297
|
+
params.set("include", options.include.join(","));
|
|
298
|
+
}
|
|
299
|
+
const queryString = params.toString();
|
|
300
|
+
const endpoint = `/api/timeback/student-mastery/${studentId}?${queryString}`;
|
|
301
|
+
return client["request"](endpoint, "GET");
|
|
275
302
|
}
|
|
276
303
|
};
|
|
277
304
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1502,9 +1502,7 @@ interface StartActivityResult {
|
|
|
1502
1502
|
|
|
1503
1503
|
/**
|
|
1504
1504
|
* A TimeBack enrollment for the current game session.
|
|
1505
|
-
* Alias for UserEnrollment without the optional gameId.
|
|
1506
|
-
* are available at `enrollment.enrollmentIds?.active` when supplied by the
|
|
1507
|
-
* platform.
|
|
1505
|
+
* Alias for UserEnrollment without the optional gameId.
|
|
1508
1506
|
*/
|
|
1509
1507
|
type TimebackEnrollment = Omit<UserEnrollment, 'gameId'>;
|
|
1510
1508
|
/**
|
|
@@ -1610,6 +1608,50 @@ interface TimebackUser extends TimebackUserContext {
|
|
|
1610
1608
|
* Call `xp.fetch()` to get XP from the server.
|
|
1611
1609
|
*/
|
|
1612
1610
|
xp: TimebackUserXp;
|
|
1611
|
+
/**
|
|
1612
|
+
* Mastery data for the current user.
|
|
1613
|
+
* Call `mastery.fetch()` to get mastery progress from the server.
|
|
1614
|
+
*/
|
|
1615
|
+
mastery: TimebackUserMastery;
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Mastery data access for the current user.
|
|
1619
|
+
* Results are cached for 5 seconds to avoid redundant network requests.
|
|
1620
|
+
*/
|
|
1621
|
+
interface TimebackUserMastery {
|
|
1622
|
+
/**
|
|
1623
|
+
* Fetch mastery data from the server.
|
|
1624
|
+
* Returns mastery for all courses in this game, or filter by grade/subject.
|
|
1625
|
+
* Results are cached for 5 seconds (use `force: true` to bypass).
|
|
1626
|
+
*
|
|
1627
|
+
* @param options - Query options
|
|
1628
|
+
* @param options.grade - Grade level to filter (must be used with subject)
|
|
1629
|
+
* @param options.subject - Subject to filter (must be used with grade)
|
|
1630
|
+
* @param options.include - Additional data to include: 'perCourse'
|
|
1631
|
+
* @param options.force - Bypass cache and fetch fresh data (default: false)
|
|
1632
|
+
* @returns Promise resolving to mastery data
|
|
1633
|
+
*
|
|
1634
|
+
* @example
|
|
1635
|
+
* ```typescript
|
|
1636
|
+
* // Get total mastery for all game courses
|
|
1637
|
+
* const mastery = await client.timeback.user.mastery.fetch()
|
|
1638
|
+
*
|
|
1639
|
+
* // Get mastery for a specific grade/subject
|
|
1640
|
+
* const mastery = await client.timeback.user.mastery.fetch({
|
|
1641
|
+
* grade: 3,
|
|
1642
|
+
* subject: 'Math'
|
|
1643
|
+
* })
|
|
1644
|
+
*
|
|
1645
|
+
* // Get mastery with per-course breakdown
|
|
1646
|
+
* const mastery = await client.timeback.user.mastery.fetch({
|
|
1647
|
+
* include: ['perCourse']
|
|
1648
|
+
* })
|
|
1649
|
+
*
|
|
1650
|
+
* // Force fresh data
|
|
1651
|
+
* const mastery = await client.timeback.user.mastery.fetch({ force: true })
|
|
1652
|
+
* ```
|
|
1653
|
+
*/
|
|
1654
|
+
fetch(options?: GetMasteryOptions): Promise<MasteryResponse>;
|
|
1613
1655
|
}
|
|
1614
1656
|
/**
|
|
1615
1657
|
* Options for querying student XP.
|
|
@@ -1624,6 +1666,19 @@ interface GetXpOptions {
|
|
|
1624
1666
|
/** Bypass cache and fetch fresh data (default: false) */
|
|
1625
1667
|
force?: boolean;
|
|
1626
1668
|
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Options for querying student mastery.
|
|
1671
|
+
*/
|
|
1672
|
+
interface GetMasteryOptions {
|
|
1673
|
+
/** Grade level to filter (must be used with subject) */
|
|
1674
|
+
grade?: TimebackGrade;
|
|
1675
|
+
/** Subject to filter (must be used with grade) */
|
|
1676
|
+
subject?: TimebackSubject;
|
|
1677
|
+
/** Additional data to include: 'perCourse' */
|
|
1678
|
+
include?: 'perCourse'[];
|
|
1679
|
+
/** Bypass cache and fetch fresh data (default: false) */
|
|
1680
|
+
force?: boolean;
|
|
1681
|
+
}
|
|
1627
1682
|
/**
|
|
1628
1683
|
* XP data for a single course.
|
|
1629
1684
|
*/
|
|
@@ -1642,6 +1697,26 @@ interface XpResponse {
|
|
|
1642
1697
|
todayXp?: number;
|
|
1643
1698
|
courses?: CourseXp[];
|
|
1644
1699
|
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Mastery data for a single course.
|
|
1702
|
+
*/
|
|
1703
|
+
interface CourseMastery {
|
|
1704
|
+
grade: TimebackGrade;
|
|
1705
|
+
subject: TimebackSubject;
|
|
1706
|
+
title: string;
|
|
1707
|
+
masteredUnits: number;
|
|
1708
|
+
masterableUnits: number;
|
|
1709
|
+
pctComplete: number;
|
|
1710
|
+
isComplete: boolean;
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Response from mastery query.
|
|
1714
|
+
*/
|
|
1715
|
+
interface MasteryResponse {
|
|
1716
|
+
totalMasteredUnits: number;
|
|
1717
|
+
totalMasterableUnits: number;
|
|
1718
|
+
courses?: CourseMastery[];
|
|
1719
|
+
}
|
|
1645
1720
|
|
|
1646
1721
|
/**
|
|
1647
1722
|
* Core client configuration and lifecycle types
|
|
@@ -2067,4 +2142,4 @@ interface AssessmentBankStatus {
|
|
|
2067
2142
|
}
|
|
2068
2143
|
|
|
2069
2144
|
export { PlaycademyClient };
|
|
2070
|
-
export type { AssessmentBankStatus, AssessmentRow, AssessmentSummary, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, ClientConfig, ClientEvents, CourseXp, DemoEndOptions, DemoEndPayload, DevUploadEvent, DevUploadHooks, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameRow as GameRecord, GameTokenResponse, GetXpOptions, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LoginResponse, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, ScoreSubmission, StartActivityOptions, StartActivityResult, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, XpResponse };
|
|
2145
|
+
export type { AssessmentBankStatus, AssessmentRow, AssessmentSummary, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, ClientConfig, ClientEvents, CourseMastery, CourseXp, DemoEndOptions, DemoEndPayload, DevUploadEvent, DevUploadHooks, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameRow as GameRecord, GameTokenResponse, GetMasteryOptions, GetXpOptions, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LoginResponse, MasteryResponse, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, ScoreSubmission, StartActivityOptions, StartActivityResult, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserMastery, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, XpResponse };
|