@playcademy/sdk 0.20.1-beta.2 → 0.20.1-beta.3
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 +1202 -26
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +3195 -85
- package/dist/internal.js +1 -1
- package/dist/server/edge.d.ts +1298 -25
- package/dist/server/edge.js +1 -1
- package/dist/server.d.ts +1298 -25
- package/dist/server.js +1 -1
- package/dist/types.d.ts +1727 -29
- package/package.json +1 -1
package/dist/server/edge.d.ts
CHANGED
|
@@ -1,10 +1,1202 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
/**
|
|
2
|
+
* D1 Database types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Schema information for database migrations
|
|
9
|
+
* Generated by comparing previous and current schema snapshots
|
|
10
|
+
*/
|
|
11
|
+
interface SchemaInfo {
|
|
12
|
+
/** SQL migration statements (DDL: CREATE, ALTER, etc.) */
|
|
13
|
+
sql: string
|
|
14
|
+
/** Hash of the schema for change detection (stringified Drizzle schema JSON) */
|
|
15
|
+
hash: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Browser permissions a game may declare and the hub may delegate to its
|
|
20
|
+
* iframe. Opt-in per game because a delegated grant keys to the hub's
|
|
21
|
+
* top-level origin: one player grant would otherwise reach every embedded
|
|
22
|
+
* game. Baseline features (fullscreen, autoplay, gamepad) are delegated to
|
|
23
|
+
* all games and are not part of this set.
|
|
24
|
+
*/
|
|
25
|
+
declare const GAME_PERMISSIONS: readonly ['microphone', 'camera'];
|
|
26
|
+
type GamePermission = (typeof GAME_PERMISSIONS)[number];
|
|
27
|
+
|
|
28
|
+
/** Every status an answering game may report for a skill. */
|
|
29
|
+
declare const SKILL_STATUSES: readonly ['locked', 'unlocked', 'in_progress', 'mastered', 'unknown_skill'];
|
|
30
|
+
/** Every way a `mastered` status may have been reached. */
|
|
31
|
+
declare const SKILL_MASTERY_SOURCES: readonly ['assessment', 'placement', 'test_out', 'manual'];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Valid grade levels per AE OneRoster GradeEnum.
|
|
35
|
+
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
36
|
+
*/
|
|
37
|
+
declare const TIMEBACK_GRADES: readonly [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
38
|
+
/**
|
|
39
|
+
* The TimeBack subject vocabulary, carried on courses, activity data, and
|
|
40
|
+
* Caliper events alike. 'None' is canonical: TimeBack accepts it as a course
|
|
41
|
+
* subject and uses it as the fallback for unknown or missing subjects.
|
|
42
|
+
*/
|
|
43
|
+
declare const TIMEBACK_SUBJECTS: readonly ['Reading', 'Language', 'Vocabulary', 'Social Studies', 'Writing', 'Science', 'FastMath', 'Math', 'None'];
|
|
44
|
+
/**
|
|
45
|
+
* Playcademy's course-specific meaning for reusable QTI test content.
|
|
46
|
+
* Result writers translate this to OneRoster assessment-result
|
|
47
|
+
* `metadata.testType`; it intentionally does not become metadata on the QTI
|
|
48
|
+
* test itself.
|
|
49
|
+
*/
|
|
50
|
+
declare const ASSESSMENT_PURPOSES: readonly ['end_of_course', 'diagnostic', 'review', 'mastery'];
|
|
51
|
+
/**
|
|
52
|
+
* Schema version of the playable assessment payload handed to a game.
|
|
53
|
+
*
|
|
54
|
+
* Distinct from an assessment's content revision: this identifies the shape of
|
|
55
|
+
* the contract, not the content inside it. Bumped when the payload gains a
|
|
56
|
+
* field or changes the meaning of one — never for content edits.
|
|
57
|
+
*
|
|
58
|
+
* Intended for diagnostics and for telling "this host does not support the
|
|
59
|
+
* field" apart from "this item does not use it". Games should branch on whether
|
|
60
|
+
* a field is present, not on this number: the content vocabularies are open by
|
|
61
|
+
* design, so feature detection keeps working across versions where a comparison
|
|
62
|
+
* does not.
|
|
63
|
+
*/
|
|
64
|
+
declare const PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION: 2;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* TimeBack Enums & Literal Types
|
|
68
|
+
*
|
|
69
|
+
* Basic type definitions used throughout the TimeBack integration. Unions
|
|
70
|
+
* derive from the canonical value lists in @playcademy/constants (type-only
|
|
71
|
+
* imports, so this package ships no runtime code).
|
|
72
|
+
*
|
|
73
|
+
* @module types/timeback/types
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The TimeBack subject vocabulary. 'None' is canonical: TimeBack accepts it
|
|
78
|
+
* on courses and uses it as the fallback for unknown or missing subjects.
|
|
79
|
+
*/
|
|
80
|
+
type TimebackSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
81
|
+
/**
|
|
82
|
+
* Grade levels per AE OneRoster GradeEnum.
|
|
83
|
+
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
84
|
+
*/
|
|
85
|
+
type TimebackGrade = (typeof TIMEBACK_GRADES)[number];
|
|
86
|
+
/**
|
|
87
|
+
* Valid Caliper subject values. The same vocabulary as OneRoster subjects.
|
|
88
|
+
*/
|
|
89
|
+
type CaliperSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
90
|
+
/**
|
|
91
|
+
* OneRoster organization types.
|
|
92
|
+
*/
|
|
93
|
+
type OrganizationType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
94
|
+
/**
|
|
95
|
+
* Lesson types for PowerPath integration.
|
|
96
|
+
*/
|
|
97
|
+
type LessonType = 'powerpath-100' | 'quiz' | 'test-out' | 'placement' | 'unit-test' | 'alpha-read-article' | null;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* TimeBack Configuration Types
|
|
101
|
+
*
|
|
102
|
+
* Configuration interfaces for Organization, Course, Component,
|
|
103
|
+
* Resource, and complete TimeBack setup.
|
|
104
|
+
*
|
|
105
|
+
* @module types/timeback/config
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Organization configuration for TimeBack (user input - optionals allowed)
|
|
110
|
+
*/
|
|
111
|
+
interface OrganizationConfig {
|
|
112
|
+
/** Display name for your organization */
|
|
113
|
+
name?: string;
|
|
114
|
+
/** Organization type */
|
|
115
|
+
type?: OrganizationType;
|
|
116
|
+
/** Unique identifier (defaults to Playcademy's org) */
|
|
117
|
+
identifier?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Course goals for daily student targets
|
|
121
|
+
*/
|
|
122
|
+
interface CourseGoals {
|
|
123
|
+
/** Target XP students should earn per day */
|
|
124
|
+
dailyXp?: number;
|
|
125
|
+
/** Target lessons per day */
|
|
126
|
+
dailyLessons?: number;
|
|
127
|
+
/** Target active minutes per day */
|
|
128
|
+
dailyActiveMinutes?: number;
|
|
129
|
+
/** Target accuracy percentage */
|
|
130
|
+
dailyAccuracy?: number;
|
|
131
|
+
/** Target mastered units per day */
|
|
132
|
+
dailyMasteredUnits?: number;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Course metrics and totals
|
|
136
|
+
*/
|
|
137
|
+
interface CourseMetrics {
|
|
138
|
+
/** Total XP available in the course */
|
|
139
|
+
totalXp?: number;
|
|
140
|
+
/** Total lessons/activities in the course */
|
|
141
|
+
totalLessons?: number;
|
|
142
|
+
/** Total number of grade levels covered by this course */
|
|
143
|
+
totalGrades?: number;
|
|
144
|
+
/** The type of course (e.g. 'optional', 'hole-filling', 'base') */
|
|
145
|
+
courseType?: 'base' | 'hole-filling' | 'optional' | 'Base' | 'Hole-Filling' | 'Optional';
|
|
146
|
+
/** Indicates whether the course is supplemental content */
|
|
147
|
+
isSupplemental?: boolean;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Complete course metadata structure
|
|
151
|
+
*/
|
|
152
|
+
interface CourseMetadata {
|
|
153
|
+
/** Define the type of course and priority for the student */
|
|
154
|
+
courseType?: 'base' | 'hole-filling' | 'optional';
|
|
155
|
+
/** Boolean value to determine if a course is supplemental to a base course */
|
|
156
|
+
isSupplemental?: boolean;
|
|
157
|
+
/** Boolean value to determine if a course is custom to an individual student */
|
|
158
|
+
isCustom?: boolean;
|
|
159
|
+
/** Signals whether a course is in production with students */
|
|
160
|
+
publishStatus?: 'draft' | 'testing' | 'published' | 'deactivated';
|
|
161
|
+
/** Whether this course appears in the TimeBack catalog for teachers and parents */
|
|
162
|
+
timebackVisible?: boolean;
|
|
163
|
+
/** Who to contact when issues reported with questions */
|
|
164
|
+
contactEmail?: string;
|
|
165
|
+
/** Primary app identifier */
|
|
166
|
+
primaryApp?: string;
|
|
167
|
+
/** Learning goals for students */
|
|
168
|
+
goals?: CourseGoals;
|
|
169
|
+
/** Course metrics and totals */
|
|
170
|
+
metrics?: CourseMetrics;
|
|
171
|
+
/** Vendor-specific metadata (e.g., AlphaLearn) */
|
|
172
|
+
[key: string]: unknown;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Course configuration for TimeBack (user input)
|
|
176
|
+
*/
|
|
177
|
+
interface CourseConfig {
|
|
178
|
+
/** Allocated OneRoster sourcedId (set after creation) */
|
|
179
|
+
sourcedId?: string;
|
|
180
|
+
/** Course title (defaults to game name) */
|
|
181
|
+
title?: string;
|
|
182
|
+
/** Subjects (REQUIRED for TimeBack integration) */
|
|
183
|
+
subjects: TimebackSubject[];
|
|
184
|
+
/** Used when recording progress/sessions if not explicitly specified per event. */
|
|
185
|
+
defaultSubject?: TimebackSubject;
|
|
186
|
+
/** Grade levels (REQUIRED for TimeBack integration) */
|
|
187
|
+
grades: TimebackGrade[];
|
|
188
|
+
/** Short course code (optional, auto-generated) */
|
|
189
|
+
courseCode?: string;
|
|
190
|
+
/** Course level (auto-derived from grades) */
|
|
191
|
+
level?: 'Elementary' | 'Middle' | 'High' | 'AP' | string;
|
|
192
|
+
/** Grading system */
|
|
193
|
+
gradingScheme?: 'STANDARD';
|
|
194
|
+
/** Total XP available in this course (REQUIRED before setup) */
|
|
195
|
+
totalXp?: number | null;
|
|
196
|
+
/** Total masterable units in this course (REQUIRED before setup) */
|
|
197
|
+
masterableUnits?: number | null;
|
|
198
|
+
/** Custom Playcademy metadata */
|
|
199
|
+
metadata?: CourseMetadata;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Component configuration for TimeBack (user input)
|
|
203
|
+
*/
|
|
204
|
+
interface ComponentConfig {
|
|
205
|
+
/** Component title (defaults to "{course.title} Activities") */
|
|
206
|
+
title?: string;
|
|
207
|
+
/** Display order */
|
|
208
|
+
sortOrder?: number;
|
|
209
|
+
/** Required prior components */
|
|
210
|
+
prerequisites?: string[];
|
|
211
|
+
/** How prerequisites work */
|
|
212
|
+
prerequisiteCriteria?: 'ALL' | 'ANY';
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Playcademy-specific resource extensions
|
|
216
|
+
*/
|
|
217
|
+
interface PlaycademyResourceMetadata {
|
|
218
|
+
/** Mastery configuration for tracking discrete learning units */
|
|
219
|
+
mastery?: {
|
|
220
|
+
/** Total number of masterable units in the resource */
|
|
221
|
+
masterableUnits: number;
|
|
222
|
+
/** Type of mastery unit for semantic clarity */
|
|
223
|
+
unitType?: 'level' | 'rank' | 'skill' | 'module';
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Resource configuration for TimeBack (user input)
|
|
228
|
+
*/
|
|
229
|
+
interface ResourceConfig {
|
|
230
|
+
/** Resource title (defaults to "{course.title} Game") */
|
|
231
|
+
title?: string;
|
|
232
|
+
/** Internal resource ID (auto-generated from package.json) */
|
|
233
|
+
vendorResourceId?: string;
|
|
234
|
+
/** Vendor identifier */
|
|
235
|
+
vendorId?: string;
|
|
236
|
+
/** Application identifier */
|
|
237
|
+
applicationId?: string;
|
|
238
|
+
/** Resource roles */
|
|
239
|
+
roles?: ('primary' | 'secondary')[];
|
|
240
|
+
/** Resource importance */
|
|
241
|
+
importance?: 'primary' | 'secondary';
|
|
242
|
+
/** Interactive resource metadata */
|
|
243
|
+
metadata?: {
|
|
244
|
+
/** Resource type */
|
|
245
|
+
type?: 'interactive';
|
|
246
|
+
/** Launch URL (defaults to Playcademy game URL) */
|
|
247
|
+
launchUrl?: string;
|
|
248
|
+
/** Platform name */
|
|
249
|
+
toolProvider?: string;
|
|
250
|
+
/** Teaching method */
|
|
251
|
+
instructionalMethod?: 'exploratory' | 'direct-instruction';
|
|
252
|
+
/** Subject area */
|
|
253
|
+
subject?: TimebackSubject;
|
|
254
|
+
/** Target grades */
|
|
255
|
+
grades?: TimebackGrade[];
|
|
256
|
+
/** Content language */
|
|
257
|
+
language?: string;
|
|
258
|
+
/** Base XP for completion */
|
|
259
|
+
xp?: number;
|
|
260
|
+
/** Playcademy-specific extensions */
|
|
261
|
+
playcademy?: PlaycademyResourceMetadata;
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Component Resource link configuration (user input)
|
|
266
|
+
*/
|
|
267
|
+
interface ComponentResourceConfig {
|
|
268
|
+
/** Link title (defaults to "{resource.title} Activity") */
|
|
269
|
+
title?: string;
|
|
270
|
+
/** Display order */
|
|
271
|
+
sortOrder?: number;
|
|
272
|
+
/** Lesson type for PowerPath integration */
|
|
273
|
+
lessonType?: LessonType;
|
|
274
|
+
}
|
|
275
|
+
interface TimebackCourseConfig {
|
|
276
|
+
subject: string;
|
|
277
|
+
grade: number;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* TimeBack Client SDK DTOs
|
|
282
|
+
*
|
|
283
|
+
* Data transfer objects for the TimeBack client SDK including
|
|
284
|
+
* progress tracking, session management, and activity completion.
|
|
285
|
+
*
|
|
286
|
+
* Note: TimebackClientConfig lives in @playcademy/timeback as it's
|
|
287
|
+
* SDK configuration, not a DTO.
|
|
288
|
+
*
|
|
289
|
+
* @module types/timeback/client
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Known extensions for TimeBack Activity Metrics Collection
|
|
294
|
+
*/
|
|
295
|
+
interface TimebackActivityExtensions {
|
|
296
|
+
/** Percentage complete (0-100) for the app course */
|
|
297
|
+
pctCompleteApp?: number;
|
|
298
|
+
/** Allow other arbitrary extensions */
|
|
299
|
+
[key: string]: unknown;
|
|
300
|
+
}
|
|
301
|
+
interface MasteryWriteWarning {
|
|
302
|
+
code: 'MASTERY_WRITE_CAPPED';
|
|
303
|
+
message: string;
|
|
304
|
+
currentMasteredUnits: number;
|
|
305
|
+
attemptedMasteredUnits: number;
|
|
306
|
+
appliedMasteredUnits: number;
|
|
307
|
+
storedMasteredUnits: number;
|
|
308
|
+
masterableUnits: number;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Activity data for ending an activity
|
|
312
|
+
*/
|
|
313
|
+
interface ActivityData {
|
|
314
|
+
/** Unique activity identifier (required) */
|
|
315
|
+
activityId: string;
|
|
316
|
+
/** Grade level for this activity (required for multi-grade course routing) */
|
|
317
|
+
grade: number;
|
|
318
|
+
/** Subject area (required for multi-grade course routing) */
|
|
319
|
+
subject: CaliperSubject;
|
|
320
|
+
/** Activity display name (optional) */
|
|
321
|
+
activityName?: string;
|
|
322
|
+
/** Course identifier (auto-filled from config if not provided) */
|
|
323
|
+
courseId?: string;
|
|
324
|
+
/** Course display name (auto-filled from config if not provided) */
|
|
325
|
+
courseName?: string;
|
|
326
|
+
/** Student email address (optional) */
|
|
327
|
+
studentEmail?: string;
|
|
328
|
+
/** Application name for Caliper events (defaults to 'Game') */
|
|
329
|
+
appName?: string;
|
|
330
|
+
/** Sensor URL for Caliper events (defaults to baseUrl) */
|
|
331
|
+
sensorUrl?: string;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Score data for activity completion
|
|
335
|
+
*/
|
|
336
|
+
interface ScoreData {
|
|
337
|
+
/** Number of questions answered correctly */
|
|
338
|
+
correctQuestions: number;
|
|
339
|
+
/** Total number of questions */
|
|
340
|
+
totalQuestions: number;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Timing data for activity completion
|
|
344
|
+
*/
|
|
345
|
+
interface TimingData {
|
|
346
|
+
/** Duration of the activity in seconds */
|
|
347
|
+
durationSeconds: number;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Complete payload for ending an activity
|
|
351
|
+
*/
|
|
352
|
+
interface EndActivityPayload {
|
|
353
|
+
/** Activity metadata */
|
|
354
|
+
activityData: ActivityData;
|
|
355
|
+
/** Score information */
|
|
356
|
+
scoreData: ScoreData;
|
|
357
|
+
/** Timing information */
|
|
358
|
+
timingData: TimingData;
|
|
359
|
+
/** XP earned for this activity */
|
|
360
|
+
xpEarned: number;
|
|
361
|
+
/** Number of learning units mastered */
|
|
362
|
+
masteredUnits?: number;
|
|
363
|
+
/** Optional arbitrary extensions to include in the Caliper event */
|
|
364
|
+
extensions?: TimebackActivityExtensions;
|
|
365
|
+
/**
|
|
366
|
+
* Stable identifier (UUID) for this activity run, enabling the platform's
|
|
367
|
+
* duplicate-completion guard: at most one completion is recorded per
|
|
368
|
+
* activity per run, and a resubmission returns `blockedByDedupeGuard`
|
|
369
|
+
* with the originally recorded award instead of double-counting XP.
|
|
370
|
+
* Without it, retried submissions may award XP twice. Use a fresh UUID
|
|
371
|
+
* per logical run — a genuine retake needs a new `runId`.
|
|
372
|
+
*/
|
|
373
|
+
runId?: string;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* TimeBack API Request/Response Types
|
|
378
|
+
*
|
|
379
|
+
* Types for TimeBack API endpoints including XP tracking,
|
|
380
|
+
* setup, verification, and activity completion.
|
|
381
|
+
*
|
|
382
|
+
* @module types/timeback/api
|
|
383
|
+
*/
|
|
384
|
+
|
|
385
|
+
interface EndActivityResponse {
|
|
386
|
+
status: 'ok';
|
|
387
|
+
courseId: string;
|
|
388
|
+
xpAwarded: number;
|
|
389
|
+
masteredUnits?: number;
|
|
390
|
+
pctCompleteApp?: number;
|
|
391
|
+
warnings?: MasteryWriteWarning[];
|
|
392
|
+
/**
|
|
393
|
+
* Set when this submission was stopped because a completion for the same
|
|
394
|
+
* run (`runId`) and activity had already been recorded — the platform's
|
|
395
|
+
* idempotency guard blocked a duplicate emit (PLA-142). This call awarded
|
|
396
|
+
* nothing new, but the award may well have happened: if the original
|
|
397
|
+
* response was lost in transit (e.g. a gateway timeout followed by an SDK
|
|
398
|
+
* retry), this is the only response the caller ever sees for a run that
|
|
399
|
+
* fully succeeded. The response therefore echoes what the run earned —
|
|
400
|
+
* `xpAwarded`, `masteredUnits`, and `pctCompleteApp` are the values
|
|
401
|
+
* recorded when the completion was confirmed (zeros, with no
|
|
402
|
+
* `pctCompleteApp`, only for runs recorded before award echoing existed).
|
|
403
|
+
* Treat it as "already recorded — here's what it earned", and do not
|
|
404
|
+
* count the run again.
|
|
405
|
+
*/
|
|
406
|
+
blockedByDedupeGuard?: boolean;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Mastery data for a single course.
|
|
410
|
+
*/
|
|
411
|
+
interface StudentCourseMastery {
|
|
412
|
+
grade: number;
|
|
413
|
+
subject: string;
|
|
414
|
+
title: string;
|
|
415
|
+
/** True mastered units from analytics. May exceed masterableUnits for historical data. */
|
|
416
|
+
masteredUnits: number;
|
|
417
|
+
/** Configured mastery ceiling for the course. */
|
|
418
|
+
masterableUnits: number;
|
|
419
|
+
/** Clamped course completion percentage from 0..100. */
|
|
420
|
+
pctComplete: number;
|
|
421
|
+
isComplete: boolean;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Response from student mastery query.
|
|
425
|
+
*/
|
|
426
|
+
interface StudentMasteryResponse {
|
|
427
|
+
/** True mastered units across all queried courses */
|
|
428
|
+
totalMasteredUnits: number;
|
|
429
|
+
/** Configured mastery ceiling across all queried courses */
|
|
430
|
+
totalMasterableUnits: number;
|
|
431
|
+
/** Per-course mastery breakdown (if requested) */
|
|
432
|
+
courses?: StudentCourseMastery[];
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* XP data for a single course.
|
|
436
|
+
*/
|
|
437
|
+
interface StudentCourseXp {
|
|
438
|
+
grade: number;
|
|
439
|
+
subject: string;
|
|
440
|
+
title: string;
|
|
441
|
+
totalXp: number;
|
|
442
|
+
todayXp?: number;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Response from student XP query.
|
|
446
|
+
*/
|
|
447
|
+
interface StudentXpResponse {
|
|
448
|
+
/** Total XP across all queried courses */
|
|
449
|
+
totalXp: number;
|
|
450
|
+
/** Today's XP (if requested) */
|
|
451
|
+
todayXp?: number;
|
|
452
|
+
/** Per-course XP breakdown (if requested) */
|
|
453
|
+
courses?: StudentCourseXp[];
|
|
454
|
+
}
|
|
455
|
+
interface StudentHighestGradeMasteredResponse {
|
|
456
|
+
/** Subject used for the highest-grade-mastered lookup */
|
|
457
|
+
subject: TimebackSubject;
|
|
458
|
+
/** EduBridge highestGradeOverall normalized to Playcademy's numeric grade type */
|
|
459
|
+
highestGradeMastered: TimebackGrade | null;
|
|
460
|
+
/** Normalized source grades returned by EduBridge for the rollup. */
|
|
461
|
+
grades: {
|
|
462
|
+
ritGrade: TimebackGrade | null;
|
|
463
|
+
edulasticGrade: TimebackGrade | null;
|
|
464
|
+
placementGrade: TimebackGrade | null;
|
|
465
|
+
testOutGrade: TimebackGrade | null;
|
|
466
|
+
highestGradeOverall: TimebackGrade | null;
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
declare const DIAGNOSTIC_TRACK_OUTCOMES: readonly ['provisional', 'unresolved', 'route-to-instruction', 'not-assessed'];
|
|
471
|
+
type DiagnosticTrackOutcome = (typeof DIAGNOSTIC_TRACK_OUTCOMES)[number];
|
|
472
|
+
type DiagnosticRoutingTrackStatus = 'pending' | 'active' | 'completed' | 'skipped';
|
|
473
|
+
type DiagnosticRoutingStatus = 'in-progress' | 'ready-to-complete';
|
|
474
|
+
interface DiagnosticRoutingNextItem {
|
|
475
|
+
stageKey: string;
|
|
476
|
+
trackKey: string;
|
|
477
|
+
nodeKey: string;
|
|
478
|
+
itemIdentifier: string;
|
|
479
|
+
}
|
|
480
|
+
/** Deliberately small semantic result exposed to normal game completion handlers. */
|
|
481
|
+
interface DiagnosticTrackResult {
|
|
482
|
+
trackKey: string;
|
|
483
|
+
groupKey?: string;
|
|
484
|
+
outcome: DiagnosticTrackOutcome;
|
|
485
|
+
resultKey: string;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** Stable curriculum-standard identity used to align and select assessment content. */
|
|
489
|
+
interface AssessmentStandardRef {
|
|
490
|
+
framework: string;
|
|
491
|
+
identifier: string;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Playcademy's course-specific meaning for reusable QTI test content.
|
|
495
|
+
* Result writers translate this to OneRoster assessment-result `metadata.testType`;
|
|
496
|
+
* it intentionally does not become metadata on the QTI test itself.
|
|
497
|
+
*/
|
|
498
|
+
type AssessmentPurpose = (typeof ASSESSMENT_PURPOSES)[number];
|
|
499
|
+
|
|
500
|
+
/** One answer value accepted by the supported QTI runtime interactions. */
|
|
501
|
+
type AssessmentResponseValue = string | string[];
|
|
502
|
+
/** Canonical response state persisted on an assessment attempt. */
|
|
503
|
+
type AssessmentResponses = Record<string, Record<string, AssessmentResponseValue>>;
|
|
504
|
+
/**
|
|
505
|
+
* One node of the rich-content vocabulary, which is OPEN: kinds are added as
|
|
506
|
+
* content that plain text would misrepresent appears, so a consumer must not
|
|
507
|
+
* treat this union as exhaustive.
|
|
508
|
+
*
|
|
509
|
+
* Structured content is projected from sanitized QTI on the host — never raw
|
|
510
|
+
* XML/HTML — and always travels alongside a flattened plain-text equivalent, so
|
|
511
|
+
* simple clients can ignore it entirely.
|
|
512
|
+
*
|
|
513
|
+
* A node carries no field common to every kind, so there is nothing to
|
|
514
|
+
* introspect on an unrecognized one. The supported fallback is one level up:
|
|
515
|
+
* abandon the whole subtree and render the enclosing `prompt` / `content`
|
|
516
|
+
* string, which is always present.
|
|
517
|
+
*
|
|
518
|
+
* Say so on screen when that happens. A question whose diagram or expression
|
|
519
|
+
* carried what was being asked still reads as complete prose once the structure
|
|
520
|
+
* is gone, so a silent fallback can leave a student an unanswerable question
|
|
521
|
+
* with nothing to indicate why.
|
|
522
|
+
*/
|
|
523
|
+
type PlayableContentNode = {
|
|
524
|
+
kind: 'text';
|
|
525
|
+
text: string;
|
|
526
|
+
} | {
|
|
527
|
+
kind: 'math';
|
|
528
|
+
/** Sanitized MathML source for clients that can render it. */
|
|
529
|
+
mathMl: string;
|
|
530
|
+
/** Plain-text approximation for clients that cannot. */
|
|
531
|
+
fallbackText: string;
|
|
532
|
+
} | {
|
|
533
|
+
kind: 'image';
|
|
534
|
+
src: string;
|
|
535
|
+
width?: number;
|
|
536
|
+
height?: number;
|
|
537
|
+
description?: string;
|
|
538
|
+
} | {
|
|
539
|
+
kind: 'paragraph';
|
|
540
|
+
children: PlayableContentNode[];
|
|
541
|
+
} | {
|
|
542
|
+
kind: 'emphasis';
|
|
543
|
+
children: PlayableContentNode[];
|
|
544
|
+
} | {
|
|
545
|
+
kind: 'strong';
|
|
546
|
+
children: PlayableContentNode[];
|
|
547
|
+
} | {
|
|
548
|
+
kind: 'table';
|
|
549
|
+
rows: {
|
|
550
|
+
header: boolean;
|
|
551
|
+
cells: PlayableContentNode[][];
|
|
552
|
+
}[];
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* A bulleted or numbered list. Modelled because flattening one to plain text
|
|
556
|
+
* genuinely changes its meaning — "one two" is not a list — unlike inline
|
|
557
|
+
* emphasis, which reads correctly either way and so is not carried.
|
|
558
|
+
*/
|
|
559
|
+
| {
|
|
560
|
+
kind: 'list';
|
|
561
|
+
/** Numbered (`ol`) rather than bulleted (`ul`). */
|
|
562
|
+
ordered: boolean;
|
|
563
|
+
items: PlayableContentNode[][];
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Where one inline interaction belongs in the prose, naming the interaction
|
|
567
|
+
* it stands for rather than relying on its position among sibling blanks.
|
|
568
|
+
*
|
|
569
|
+
* The identifier is what makes this robust: matching blanks to interactions
|
|
570
|
+
* by order silently mispairs them when either sequence shifts, and a
|
|
571
|
+
* mispaired dropdown produces a wrong answer rather than a visible error.
|
|
572
|
+
*/
|
|
573
|
+
| {
|
|
574
|
+
kind: 'blank';
|
|
575
|
+
responseIdentifier: string;
|
|
576
|
+
}
|
|
577
|
+
/** One named target inside a gap-match passage. */
|
|
578
|
+
| {
|
|
579
|
+
kind: 'gap';
|
|
580
|
+
identifier: string;
|
|
581
|
+
};
|
|
582
|
+
interface PlayableAssessmentChoice {
|
|
583
|
+
identifier: string;
|
|
584
|
+
content: string;
|
|
585
|
+
/** Keep this choice in its authored slot when the interaction is shuffled. */
|
|
586
|
+
fixed?: boolean;
|
|
587
|
+
/** Rich content when the choice carries more than plain text. */
|
|
588
|
+
contentNodes?: PlayableContentNode[];
|
|
589
|
+
}
|
|
590
|
+
/** A Match choice, which unlike an ordinary choice carries an association capacity. */
|
|
591
|
+
interface PlayableMatchChoice extends PlayableAssessmentChoice {
|
|
592
|
+
/** Associations this choice accepts (0 means unlimited). */
|
|
593
|
+
matchMax: number;
|
|
594
|
+
}
|
|
595
|
+
interface PlayableAssessmentInteractionBase {
|
|
596
|
+
responseIdentifier: string;
|
|
597
|
+
/** Prompt declared inside the interaction (QTI qti-prompt), when present. */
|
|
598
|
+
prompt?: string;
|
|
599
|
+
}
|
|
600
|
+
interface PlayableChoiceInteraction extends PlayableAssessmentInteractionBase {
|
|
601
|
+
type: 'choice';
|
|
602
|
+
choices: PlayableAssessmentChoice[];
|
|
603
|
+
/** The host has already applied attempt-stable ordering; render the received array. */
|
|
604
|
+
shuffle: boolean;
|
|
605
|
+
minChoices: number;
|
|
606
|
+
maxChoices: number;
|
|
607
|
+
}
|
|
608
|
+
interface PlayableInlineChoiceInteraction extends PlayableAssessmentInteractionBase {
|
|
609
|
+
type: 'inline-choice';
|
|
610
|
+
choices: PlayableAssessmentChoice[];
|
|
611
|
+
/** The host has already applied attempt-stable ordering; render the received array. */
|
|
612
|
+
shuffle: boolean;
|
|
613
|
+
}
|
|
614
|
+
interface PlayableTextEntryInteraction extends PlayableAssessmentInteractionBase {
|
|
615
|
+
type: 'text-entry';
|
|
616
|
+
expectedLength?: number;
|
|
617
|
+
placeholder?: string;
|
|
618
|
+
}
|
|
619
|
+
interface PlayableOrderInteraction extends PlayableAssessmentInteractionBase {
|
|
620
|
+
type: 'order';
|
|
621
|
+
choices: PlayableAssessmentChoice[];
|
|
622
|
+
/** Whether the host applies attempt-stable presentation ordering. */
|
|
623
|
+
shuffle: boolean;
|
|
624
|
+
}
|
|
625
|
+
interface PlayableMatchInteraction extends PlayableAssessmentInteractionBase {
|
|
626
|
+
type: 'match';
|
|
627
|
+
sourceChoices: PlayableMatchChoice[];
|
|
628
|
+
targetChoices: PlayableMatchChoice[];
|
|
629
|
+
maxAssociations: number;
|
|
630
|
+
/** Whether the host applies independent attempt-stable ordering to both choice sets. */
|
|
631
|
+
shuffle: boolean;
|
|
632
|
+
}
|
|
633
|
+
interface PlayableHottextSegment {
|
|
634
|
+
identifier?: string;
|
|
635
|
+
content: string;
|
|
636
|
+
selectable: boolean;
|
|
637
|
+
}
|
|
638
|
+
interface PlayableHottextInteraction extends PlayableAssessmentInteractionBase {
|
|
639
|
+
type: 'hottext';
|
|
640
|
+
segments: PlayableHottextSegment[];
|
|
641
|
+
maxChoices: number;
|
|
642
|
+
}
|
|
643
|
+
/** A sanitized graphic reference rendered by graphic interactions. */
|
|
644
|
+
interface PlayableAssessmentGraphic {
|
|
645
|
+
src: string;
|
|
646
|
+
width?: number;
|
|
647
|
+
height?: number;
|
|
648
|
+
/** Accessible description carried from the source QTI. */
|
|
649
|
+
description?: string;
|
|
650
|
+
}
|
|
651
|
+
/** The QTI region shape vocabulary shared by hotspots and scored areas. */
|
|
652
|
+
declare const PLAYABLE_ASSESSMENT_SHAPES: readonly ['circle', 'ellipse', 'rect', 'poly', 'default'];
|
|
653
|
+
type PlayableAssessmentShape = (typeof PLAYABLE_ASSESSMENT_SHAPES)[number];
|
|
654
|
+
/** A clickable or associable region positioned on a graphic. */
|
|
655
|
+
interface PlayableAssessmentHotspot {
|
|
656
|
+
identifier: string;
|
|
657
|
+
shape: PlayableAssessmentShape;
|
|
658
|
+
coords: number[];
|
|
659
|
+
/** Maximum associations this region accepts (0 or absent means unlimited). */
|
|
660
|
+
matchMax?: number;
|
|
661
|
+
description?: string;
|
|
662
|
+
}
|
|
663
|
+
interface PlayableHotspotInteraction extends PlayableAssessmentInteractionBase {
|
|
664
|
+
type: 'hotspot';
|
|
665
|
+
image: PlayableAssessmentGraphic;
|
|
666
|
+
hotspots: PlayableAssessmentHotspot[];
|
|
667
|
+
minChoices: number;
|
|
668
|
+
/** Always a concrete cap: an authored 0 (QTI: unlimited) resolves to the region count. */
|
|
669
|
+
maxChoices: number;
|
|
670
|
+
}
|
|
671
|
+
interface PlayableGapMatchChoice {
|
|
672
|
+
identifier: string;
|
|
673
|
+
content: string;
|
|
674
|
+
/** Keep this token in its authored bank slot, not a passage gap. */
|
|
675
|
+
fixed?: boolean;
|
|
676
|
+
/** Times this token may be placed (0 means unlimited). */
|
|
677
|
+
matchMax: number;
|
|
678
|
+
}
|
|
679
|
+
interface PlayableGapMatchInteraction extends PlayableAssessmentInteractionBase {
|
|
680
|
+
type: 'gap-match';
|
|
681
|
+
/** Shuffle the token bank without moving passage gaps. */
|
|
682
|
+
shuffle: boolean;
|
|
683
|
+
/** Passage containing each gap as a `___` blank, in `gaps` order. */
|
|
684
|
+
content: string;
|
|
685
|
+
/** Structured passage retaining each named gap at its authored inline position. */
|
|
686
|
+
contentNodes: PlayableContentNode[];
|
|
687
|
+
gapTexts: PlayableGapMatchChoice[];
|
|
688
|
+
/** Ordered gap identifiers matching the passage blanks. */
|
|
689
|
+
gaps: string[];
|
|
690
|
+
/** Overall pair limit (0 means unlimited). */
|
|
691
|
+
maxAssociations: number;
|
|
692
|
+
}
|
|
693
|
+
interface PlayableGapImageChoice {
|
|
694
|
+
identifier: string;
|
|
695
|
+
image?: PlayableAssessmentGraphic;
|
|
696
|
+
/** Times this token may be placed (0 means unlimited). */
|
|
697
|
+
matchMax: number;
|
|
698
|
+
description?: string;
|
|
699
|
+
}
|
|
700
|
+
interface PlayableGraphicGapMatchInteraction extends PlayableAssessmentInteractionBase {
|
|
701
|
+
type: 'graphic-gap-match';
|
|
702
|
+
image: PlayableAssessmentGraphic;
|
|
703
|
+
gapImages: PlayableGapImageChoice[];
|
|
704
|
+
hotspots: PlayableAssessmentHotspot[];
|
|
705
|
+
/** Overall pair limit (0 means unlimited). */
|
|
706
|
+
maxAssociations: number;
|
|
707
|
+
}
|
|
708
|
+
interface PlayableSelectPointInteraction extends PlayableAssessmentInteractionBase {
|
|
709
|
+
type: 'select-point';
|
|
710
|
+
image: PlayableAssessmentGraphic;
|
|
711
|
+
minChoices: number;
|
|
712
|
+
maxChoices: number;
|
|
713
|
+
}
|
|
714
|
+
interface PlayableGraphicAssociateInteraction extends PlayableAssessmentInteractionBase {
|
|
715
|
+
type: 'graphic-associate';
|
|
716
|
+
image: PlayableAssessmentGraphic;
|
|
717
|
+
hotspots: PlayableAssessmentHotspot[];
|
|
718
|
+
maxAssociations: number;
|
|
719
|
+
}
|
|
720
|
+
interface PlayablePositionObjectInteraction extends PlayableAssessmentInteractionBase {
|
|
721
|
+
type: 'position-object';
|
|
722
|
+
/** Background stage the object is placed onto. */
|
|
723
|
+
stage: PlayableAssessmentGraphic;
|
|
724
|
+
/** The movable object image. */
|
|
725
|
+
object: PlayableAssessmentGraphic;
|
|
726
|
+
/** Maximum number of placements. */
|
|
727
|
+
maxPlacements: number;
|
|
728
|
+
}
|
|
729
|
+
type PlayableAssessmentInteraction = PlayableChoiceInteraction | PlayableInlineChoiceInteraction | PlayableTextEntryInteraction | PlayableOrderInteraction | PlayableMatchInteraction | PlayableHottextInteraction | PlayableHotspotInteraction | PlayableGapMatchInteraction | PlayableGraphicGapMatchInteraction | PlayableSelectPointInteraction | PlayableGraphicAssociateInteraction | PlayablePositionObjectInteraction;
|
|
730
|
+
interface PlayableAssessmentItem {
|
|
731
|
+
identifier: string;
|
|
732
|
+
title: string;
|
|
733
|
+
/**
|
|
734
|
+
* Shared item stem, flattened to plain text. Inline interactions appear as
|
|
735
|
+
* `___`, and block interactions belong after the prompt in `interactions`
|
|
736
|
+
* order.
|
|
737
|
+
*
|
|
738
|
+
* The `___` marks that a blank is there, not which interaction fills it or
|
|
739
|
+
* exactly where it sits: word offsets into this string are not part of the
|
|
740
|
+
* contract. Prose is translated, re-wrapped, and tokenized differently by
|
|
741
|
+
* language — a position counted in words does not survive any of that, and
|
|
742
|
+
* languages that do not separate words with spaces have no such position to
|
|
743
|
+
* count. Read `promptContent` to place blanks precisely; a client that
|
|
744
|
+
* ignores it should render inline interactions after the prompt.
|
|
745
|
+
*/
|
|
746
|
+
prompt: string;
|
|
747
|
+
/**
|
|
748
|
+
* Structured prompt content, present whenever the stem holds an inline
|
|
749
|
+
* interaction or anything plain text would misrepresent — images, tables,
|
|
750
|
+
* lists, MathML. Absent means `prompt` says everything.
|
|
751
|
+
*
|
|
752
|
+
* Inline interactions appear as `blank` nodes carrying the response
|
|
753
|
+
* identifier they belong to.
|
|
754
|
+
*/
|
|
755
|
+
promptContent?: PlayableContentNode[];
|
|
756
|
+
/** Maximum points awarded by the item's declared outcome (1 when undeclared). */
|
|
757
|
+
maxScore: number;
|
|
758
|
+
/** Document-ordered interactions with item-unique response identifiers. */
|
|
759
|
+
interactions: PlayableAssessmentInteraction[];
|
|
760
|
+
}
|
|
761
|
+
/** Sanitized assessment content safe to return to a sandboxed game. */
|
|
762
|
+
interface PlayableAssessment {
|
|
763
|
+
identifier: string;
|
|
764
|
+
/**
|
|
765
|
+
* Schema version of this payload's shape, never of its content.
|
|
766
|
+
*
|
|
767
|
+
* Present so a game can tell a field the host does not send from one this
|
|
768
|
+
* item does not use, and so a mismatch is legible in a bug report. Branch on
|
|
769
|
+
* whether a field is present rather than on this number — the content
|
|
770
|
+
* vocabularies are deliberately open, so feature detection survives
|
|
771
|
+
* versions that a comparison against a fixed number does not.
|
|
772
|
+
*/
|
|
773
|
+
contractVersion: typeof PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION;
|
|
774
|
+
/**
|
|
775
|
+
* Opaque revision of this assessment's content, used to ensure a resumed
|
|
776
|
+
* attempt loads what it started with. Changes when an author edits the
|
|
777
|
+
* assessment; unrelated to `contractVersion`.
|
|
778
|
+
*/
|
|
779
|
+
contentRevision: string;
|
|
780
|
+
title: string;
|
|
781
|
+
items: PlayableAssessmentItem[];
|
|
782
|
+
}
|
|
783
|
+
interface AssessmentScore {
|
|
784
|
+
earned: number;
|
|
785
|
+
possible: number;
|
|
786
|
+
/** Aggregate score normalized to the inclusive 0..1 range. */
|
|
787
|
+
normalized: number;
|
|
788
|
+
}
|
|
789
|
+
type FixedAssessmentPurpose = Exclude<AssessmentPurpose, 'review'>;
|
|
790
|
+
/** Fixed-test purposes that do not select their catalog by curriculum standard. */
|
|
791
|
+
type UnscopedFixedAssessmentPurpose = Exclude<FixedAssessmentPurpose, 'mastery'>;
|
|
792
|
+
/** One requested standard slot and the playable bank item selected for it. */
|
|
793
|
+
interface AssessmentReviewItemSelection {
|
|
794
|
+
standard: AssessmentStandardRef;
|
|
795
|
+
itemIdentifier: string;
|
|
796
|
+
}
|
|
797
|
+
/** Why one requested standard could not be fully served by the current bank. */
|
|
798
|
+
interface AssessmentReviewShortage {
|
|
799
|
+
standard: AssessmentStandardRef;
|
|
800
|
+
requested: number;
|
|
801
|
+
selected: number;
|
|
802
|
+
eligible: number;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Explicit review-request fulfillment. A partial result still starts a normal
|
|
806
|
+
* assessment attempt with every selectable item; no missing standard is
|
|
807
|
+
* silently omitted.
|
|
808
|
+
*/
|
|
809
|
+
type AssessmentReviewFulfillment = {
|
|
810
|
+
status: 'complete';
|
|
811
|
+
requestedItemCount: number;
|
|
812
|
+
selectedItemCount: number;
|
|
813
|
+
shortages: [];
|
|
814
|
+
} | {
|
|
815
|
+
status: 'partial';
|
|
816
|
+
requestedItemCount: number;
|
|
817
|
+
selectedItemCount: number;
|
|
818
|
+
shortages: AssessmentReviewShortage[];
|
|
819
|
+
};
|
|
820
|
+
interface FixedAssessmentSelectionContext {
|
|
821
|
+
kind: 'fixed-test';
|
|
822
|
+
purpose: UnscopedFixedAssessmentPurpose;
|
|
823
|
+
}
|
|
824
|
+
/** A complete fixed quiz selected from the catalog for one canonical standard. */
|
|
825
|
+
interface StandardQuizSelectionContext {
|
|
826
|
+
kind: 'standard-quiz';
|
|
827
|
+
purpose: 'mastery';
|
|
828
|
+
standard: AssessmentStandardRef;
|
|
829
|
+
}
|
|
830
|
+
/** Administration policy for one requested review skill, separate from its identity. */
|
|
831
|
+
interface AssessmentReviewStandard extends AssessmentStandardRef {
|
|
832
|
+
/** Require this skill's second selected question, subject to the player's cap. */
|
|
833
|
+
requireBothQuestions?: boolean;
|
|
834
|
+
}
|
|
835
|
+
interface StandardsReviewSelectionContext {
|
|
836
|
+
kind: 'standards-review';
|
|
837
|
+
purpose: 'review';
|
|
838
|
+
standards: AssessmentReviewStandard[];
|
|
839
|
+
candidateItemsPerStandard: number;
|
|
840
|
+
selections: AssessmentReviewItemSelection[];
|
|
841
|
+
fulfillment: AssessmentReviewFulfillment;
|
|
842
|
+
}
|
|
843
|
+
/** Immutable identity pinned when the host starts a platform-routed diagnostic. */
|
|
844
|
+
interface PlatformRoutedDiagnosticSelectionContext {
|
|
845
|
+
kind: 'platform-routed-diagnostic';
|
|
846
|
+
purpose: 'diagnostic';
|
|
847
|
+
definitionId: string;
|
|
848
|
+
diagnosticKey: string;
|
|
849
|
+
routingRevision: string;
|
|
850
|
+
}
|
|
851
|
+
/** How the host assembled the playable assessment returned for this attempt. */
|
|
852
|
+
type AssessmentSelectionContext = FixedAssessmentSelectionContext | StandardQuizSelectionContext | StandardsReviewSelectionContext | PlatformRoutedDiagnosticSelectionContext;
|
|
853
|
+
/** Server-derived assessment interaction model; callers select it only through purpose. */
|
|
854
|
+
type AssessmentFlow = 'attempt-submit' | 'item-submit' | 'platform-routed-item-submit';
|
|
855
|
+
/** Safe feedback persisted when one item is committed in an item-submit flow. */
|
|
856
|
+
type AssessmentItemGrading = {
|
|
857
|
+
source: 'timeback-qti';
|
|
858
|
+
graderVersion: string;
|
|
859
|
+
} | {
|
|
860
|
+
source: 'platform-qti-adapter';
|
|
861
|
+
graderVersion: string;
|
|
862
|
+
qtiGraderVersion: string;
|
|
863
|
+
} | {
|
|
864
|
+
source: 'platform-artifact';
|
|
865
|
+
artifactVersion: string;
|
|
866
|
+
graderVersion: string;
|
|
867
|
+
};
|
|
868
|
+
interface AssessmentItemSubmission {
|
|
869
|
+
/** Stable caller-generated key used to replay a lost success response. */
|
|
870
|
+
submissionId: string;
|
|
871
|
+
itemIdentifier: string;
|
|
872
|
+
/** Stable administration timestamp used for exposure ordering and resume reconciliation. */
|
|
873
|
+
submittedAt: string;
|
|
874
|
+
/** One-based position in the validated administration sequence. */
|
|
875
|
+
responseVersion: number;
|
|
876
|
+
answered: boolean;
|
|
877
|
+
score: AssessmentScore;
|
|
878
|
+
isCorrect: boolean | null;
|
|
879
|
+
/**
|
|
880
|
+
* Trusted grading provenance pinned at grading time so child-result repair
|
|
881
|
+
* reproduces the corresponding child projection. Absent only on attempts
|
|
882
|
+
* committed before provenance was introduced.
|
|
883
|
+
*/
|
|
884
|
+
grading?: AssessmentItemGrading;
|
|
885
|
+
}
|
|
886
|
+
/** Raw answers only. Array order is the administration sequence; no grades are trusted. */
|
|
887
|
+
interface AssessmentTranscript {
|
|
888
|
+
version: 1;
|
|
889
|
+
attemptId: string;
|
|
890
|
+
/** The selected source revision, not a review subset's presentation revision. */
|
|
891
|
+
contentRevision: string;
|
|
892
|
+
administrations: {
|
|
893
|
+
submissionId: string;
|
|
894
|
+
itemIdentifier: string;
|
|
895
|
+
routingNodeKey?: string;
|
|
896
|
+
/** An empty response is still an administered question. */
|
|
897
|
+
responses: Record<string, AssessmentResponseValue>;
|
|
898
|
+
}[];
|
|
899
|
+
}
|
|
900
|
+
interface ResumeAssessmentInput {
|
|
901
|
+
/** Omit to recover the parent checkpoint, or best-effort child progress if none exists. */
|
|
902
|
+
transcript?: AssessmentTranscript;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Canonical activity context the SDK uses to track time for this attempt. A
|
|
906
|
+
* narrowed `ActivityData`, so it can be handed straight to the shared clock:
|
|
907
|
+
* the course routing fields are resolved rather than optional.
|
|
908
|
+
*/
|
|
909
|
+
type AssessmentActivityData = Pick<ActivityData, 'activityId' | 'courseId'> & {
|
|
910
|
+
activityName: string;
|
|
911
|
+
grade: TimebackGrade;
|
|
912
|
+
subject: TimebackSubject;
|
|
913
|
+
};
|
|
914
|
+
interface AssessmentAttemptSnapshotBase {
|
|
915
|
+
attemptId: string;
|
|
916
|
+
responseVersion: number;
|
|
917
|
+
status: 'in_progress';
|
|
918
|
+
/** Resolved server context for the shared activity-session clock, when valid. */
|
|
919
|
+
activityData?: AssessmentActivityData;
|
|
920
|
+
assessment: PlayableAssessment;
|
|
921
|
+
/** Opaque, attempt-scoped authorization. Renew through start/resume, not per answer. */
|
|
922
|
+
assessmentToken: string;
|
|
923
|
+
transcript: AssessmentTranscript;
|
|
924
|
+
}
|
|
925
|
+
/** Existing fixed, mastery, and review attempt shape. */
|
|
926
|
+
interface ConventionalAssessmentAttemptSnapshot extends AssessmentAttemptSnapshotBase {
|
|
927
|
+
/** Historical fixed diagnostics remain attempt-submit and are never reinterpreted. */
|
|
928
|
+
flow: Exclude<AssessmentFlow, 'platform-routed-item-submit'>;
|
|
929
|
+
responses: AssessmentResponses;
|
|
930
|
+
/** Ordered committed-item ledger. Empty for attempt-submit flows. */
|
|
931
|
+
itemSubmissions: AssessmentItemSubmission[];
|
|
932
|
+
score: AssessmentScore | null;
|
|
933
|
+
selection: Exclude<AssessmentSelectionContext, PlatformRoutedDiagnosticSelectionContext>;
|
|
934
|
+
}
|
|
935
|
+
/** Safe public track progress; authored terminal semantics stay host-only until completion. */
|
|
936
|
+
interface DiagnosticRoutingTrackSnapshot {
|
|
937
|
+
trackKey: string;
|
|
938
|
+
groupKey?: string;
|
|
939
|
+
status: DiagnosticRoutingTrackStatus;
|
|
940
|
+
administeredCount: number;
|
|
941
|
+
}
|
|
942
|
+
/** Safe canonical routing projection returned while a diagnostic is in progress. */
|
|
943
|
+
interface DiagnosticRoutingSnapshot {
|
|
944
|
+
kind: 'platform-routed-diagnostic';
|
|
945
|
+
revision: string;
|
|
946
|
+
status: DiagnosticRoutingStatus;
|
|
947
|
+
next: DiagnosticRoutingNextItem | null;
|
|
948
|
+
tracks: DiagnosticRoutingTrackSnapshot[];
|
|
949
|
+
}
|
|
950
|
+
/** Small semantic diagnostic result consumed by the game after finalization. */
|
|
951
|
+
interface DiagnosticAssessmentSubmitResult {
|
|
952
|
+
purpose: 'diagnostic';
|
|
953
|
+
attemptId: string;
|
|
954
|
+
submissionId: string;
|
|
955
|
+
diagnosticKey: string;
|
|
956
|
+
responseVersion: number;
|
|
957
|
+
status: 'awaiting_award';
|
|
958
|
+
tracks: DiagnosticTrackResult[];
|
|
959
|
+
}
|
|
960
|
+
/** Platform-routed diagnostics never expose grading, feedback, or their committed ledger. */
|
|
961
|
+
interface PlatformRoutedDiagnosticAttemptSnapshot extends AssessmentAttemptSnapshotBase {
|
|
962
|
+
flow: 'platform-routed-item-submit';
|
|
963
|
+
selection: PlatformRoutedDiagnosticSelectionContext;
|
|
964
|
+
routing: DiagnosticRoutingSnapshot;
|
|
965
|
+
completion: null;
|
|
966
|
+
}
|
|
967
|
+
interface AssessmentSaveResult {
|
|
968
|
+
attemptId: string;
|
|
969
|
+
responseVersion: number;
|
|
970
|
+
status: 'in_progress';
|
|
971
|
+
responses: AssessmentResponses;
|
|
972
|
+
transcript: AssessmentTranscript;
|
|
973
|
+
}
|
|
974
|
+
interface AssessmentSubmitResultBase {
|
|
975
|
+
attemptId: string;
|
|
976
|
+
submissionId: string;
|
|
977
|
+
responseVersion: number;
|
|
978
|
+
status: 'awaiting_award';
|
|
979
|
+
responses: AssessmentResponses;
|
|
980
|
+
score: AssessmentScore;
|
|
981
|
+
}
|
|
982
|
+
interface FixedAssessmentSubmitResult extends AssessmentSubmitResultBase {
|
|
983
|
+
purpose: UnscopedFixedAssessmentPurpose;
|
|
984
|
+
}
|
|
985
|
+
/** Safe final item grade, exposed only after the assessment is submitted. */
|
|
986
|
+
interface AssessmentItemOutcome {
|
|
987
|
+
itemIdentifier: string;
|
|
988
|
+
standard: AssessmentStandardRef;
|
|
989
|
+
answered: boolean;
|
|
990
|
+
score: AssessmentScore;
|
|
991
|
+
isCorrect: boolean | null;
|
|
992
|
+
}
|
|
993
|
+
interface ReviewAssessmentSubmitResult extends AssessmentSubmitResultBase {
|
|
994
|
+
purpose: 'review';
|
|
995
|
+
/** Final outcomes in committed transcript order, excluding unused candidates. */
|
|
996
|
+
itemOutcomes: AssessmentItemOutcome[];
|
|
997
|
+
}
|
|
998
|
+
interface MasteryAssessmentSubmitResult extends AssessmentSubmitResultBase {
|
|
999
|
+
purpose: 'mastery';
|
|
1000
|
+
/** One final outcome per question in test order, including unanswered questions. */
|
|
1001
|
+
itemOutcomes: AssessmentItemOutcome[];
|
|
1002
|
+
}
|
|
1003
|
+
type ConventionalAssessmentSubmitResult = FixedAssessmentSubmitResult | ReviewAssessmentSubmitResult | MasteryAssessmentSubmitResult;
|
|
1004
|
+
type AssessmentSubmitResult = ConventionalAssessmentSubmitResult | DiagnosticAssessmentSubmitResult;
|
|
1005
|
+
type CompletedAssessmentResult<TResult extends AssessmentSubmitResult> = Omit<TResult, 'status'> & {
|
|
1006
|
+
status: 'completed';
|
|
1007
|
+
/** XP durably recorded on the authoritative OneRoster AssessmentResult. */
|
|
1008
|
+
xpAwarded: number;
|
|
1009
|
+
/**
|
|
1010
|
+
* The mastery delta durably recorded on the authoritative result after
|
|
1011
|
+
* platform bounding. Zero when the game awarded no mastery.
|
|
1012
|
+
*/
|
|
1013
|
+
masteredUnitsApplied: number;
|
|
1014
|
+
/** Course completion percentage after this award, when mastery tracking is configured. */
|
|
1015
|
+
pctCompleteApp?: number;
|
|
1016
|
+
/** Non-fatal bounding warnings raised while applying the award. */
|
|
1017
|
+
warnings?: MasteryWriteWarning[];
|
|
1018
|
+
};
|
|
1019
|
+
/** Terminal result returned only after the game's award has been recorded. */
|
|
1020
|
+
type AssessmentFinalizeResult = CompletedAssessmentResult<FixedAssessmentSubmitResult> | CompletedAssessmentResult<ReviewAssessmentSubmitResult> | CompletedAssessmentResult<MasteryAssessmentSubmitResult> | CompletedAssessmentResult<DiagnosticAssessmentSubmitResult>;
|
|
1021
|
+
/** Minimal recovery snapshot; no QTI content load is required after answers are locked. */
|
|
1022
|
+
type SettledAssessmentAttemptSnapshot = {
|
|
1023
|
+
attemptId: string;
|
|
1024
|
+
responseVersion: number;
|
|
1025
|
+
status: 'awaiting_award';
|
|
1026
|
+
result: AssessmentSubmitResult;
|
|
1027
|
+
} | {
|
|
1028
|
+
attemptId: string;
|
|
1029
|
+
responseVersion: number;
|
|
1030
|
+
status: 'completed';
|
|
1031
|
+
result: AssessmentFinalizeResult;
|
|
1032
|
+
};
|
|
1033
|
+
/** Snapshot states that are still open for responses. */
|
|
1034
|
+
type PlayableAssessmentAttemptSnapshot = ConventionalAssessmentAttemptSnapshot | PlatformRoutedDiagnosticAttemptSnapshot;
|
|
1035
|
+
type AssessmentAttemptSnapshot = PlayableAssessmentAttemptSnapshot | SettledAssessmentAttemptSnapshot;
|
|
1036
|
+
interface GetLatestAssessmentOptionsBase {
|
|
1037
|
+
subject?: TimebackSubject;
|
|
1038
|
+
grade?: TimebackGrade;
|
|
1039
|
+
}
|
|
1040
|
+
/** Filters for finding the authenticated student's latest completed assessment. */
|
|
1041
|
+
type GetLatestAssessmentOptions = (GetLatestAssessmentOptionsBase & {
|
|
1042
|
+
purpose: Exclude<AssessmentPurpose, 'mastery'>;
|
|
1043
|
+
standard?: never;
|
|
1044
|
+
}) | (GetLatestAssessmentOptionsBase & {
|
|
1045
|
+
purpose: 'mastery';
|
|
1046
|
+
standard: AssessmentStandardRef;
|
|
1047
|
+
});
|
|
1048
|
+
/** Compact result returned by the latest-completed-assessment lookup. */
|
|
1049
|
+
interface LatestAssessmentResult {
|
|
1050
|
+
attemptId: string;
|
|
1051
|
+
purpose: AssessmentPurpose;
|
|
1052
|
+
activityId: string;
|
|
1053
|
+
subject: TimebackSubject;
|
|
1054
|
+
grade: TimebackGrade;
|
|
1055
|
+
testIdentifier: string;
|
|
1056
|
+
/** Canonical request identity for mastery results; null for other purposes. */
|
|
1057
|
+
standard: AssessmentStandardRef | null;
|
|
1058
|
+
completedAt: string;
|
|
1059
|
+
/** Persisted aggregate score normalized to the inclusive 0..1 range. */
|
|
1060
|
+
score: Pick<AssessmentScore, 'normalized'>;
|
|
1061
|
+
}
|
|
1062
|
+
interface StartAssessmentInputBase {
|
|
1063
|
+
activityId: string;
|
|
1064
|
+
subject?: TimebackSubject;
|
|
1065
|
+
grade?: TimebackGrade;
|
|
1066
|
+
}
|
|
1067
|
+
interface StartFixedAssessmentInput extends StartAssessmentInputBase {
|
|
1068
|
+
purpose: Exclude<UnscopedFixedAssessmentPurpose, 'diagnostic'>;
|
|
1069
|
+
}
|
|
1070
|
+
interface StartDiagnosticAssessmentInput extends StartAssessmentInputBase {
|
|
1071
|
+
purpose: 'diagnostic';
|
|
1072
|
+
/** Stable authored diagnostic identity, independent of the selected QTI test identifier. */
|
|
1073
|
+
diagnosticKey: string;
|
|
1074
|
+
}
|
|
1075
|
+
interface StartMasteryAssessmentInput extends StartAssessmentInputBase {
|
|
1076
|
+
purpose: 'mastery';
|
|
1077
|
+
standard: AssessmentStandardRef;
|
|
1078
|
+
}
|
|
1079
|
+
interface StartReviewAssessmentInput extends StartAssessmentInputBase {
|
|
1080
|
+
purpose: 'review';
|
|
1081
|
+
/** Serving order; duplicate canonical skills keep their first position. Pinned on start. */
|
|
1082
|
+
standards: AssessmentReviewStandard[];
|
|
1083
|
+
/** Candidate capacity per standard; preloaded candidates are not administered automatically. */
|
|
1084
|
+
candidateItemsPerStandard?: number;
|
|
1085
|
+
}
|
|
1086
|
+
type StartAssessmentInput = StartFixedAssessmentInput | StartDiagnosticAssessmentInput | StartMasteryAssessmentInput | StartReviewAssessmentInput;
|
|
1087
|
+
/** Transport metadata for attempting a prepared new-assessment start. */
|
|
1088
|
+
interface StartAssessmentOptions {
|
|
1089
|
+
preparationReceipt?: string;
|
|
1090
|
+
}
|
|
1091
|
+
/** Result of preparing the immediate next assessment request. */
|
|
1092
|
+
type AssessmentPreparationResult = {
|
|
1093
|
+
status: 'prepared';
|
|
1094
|
+
receipt: string;
|
|
1095
|
+
/** Receipt expiry as an ISO-8601 timestamp. */
|
|
1096
|
+
expiresAt: string;
|
|
1097
|
+
} | {
|
|
1098
|
+
status: 'unprepared';
|
|
1099
|
+
reason: 'artifact_unavailable' | 'prerequisite_unavailable' | 'local_runtime';
|
|
1100
|
+
};
|
|
1101
|
+
interface SaveAssessmentInput {
|
|
1102
|
+
expectedResponseVersion: number;
|
|
1103
|
+
/** Complete active draft; success acknowledges its durable parent checkpoint. */
|
|
1104
|
+
transcript: AssessmentTranscript;
|
|
1105
|
+
}
|
|
1106
|
+
/** Best-effort mastery progress, not an answer commitment or an explicit checkpoint. */
|
|
1107
|
+
interface RecordAssessmentProgressInput {
|
|
1108
|
+
assessmentToken: string;
|
|
1109
|
+
expectedResponseVersion: number;
|
|
1110
|
+
/** Current raw draft, including the editable administration being projected. */
|
|
1111
|
+
transcript: AssessmentTranscript;
|
|
1112
|
+
itemIdentifier: string;
|
|
1113
|
+
}
|
|
1114
|
+
interface SubmitAssessmentItemInputBase {
|
|
1115
|
+
assessmentToken: string;
|
|
1116
|
+
/** The raw prefix preceding this administration (or including it for an exact retry). */
|
|
1117
|
+
transcript: AssessmentTranscript;
|
|
1118
|
+
expectedResponseVersion: number;
|
|
1119
|
+
/** Stable across every retry of this exact item submission. */
|
|
1120
|
+
submissionId: string;
|
|
1121
|
+
itemIdentifier: string;
|
|
1122
|
+
responses: Record<string, AssessmentResponseValue | null>;
|
|
1123
|
+
}
|
|
1124
|
+
interface SubmitReviewAssessmentItemInput extends SubmitAssessmentItemInputBase {
|
|
1125
|
+
routingNodeKey?: never;
|
|
1126
|
+
}
|
|
1127
|
+
interface SubmitDiagnosticAssessmentItemInput extends SubmitAssessmentItemInputBase {
|
|
1128
|
+
/** Canonical routing-context identity returned in routing.next. */
|
|
1129
|
+
routingNodeKey: string;
|
|
1130
|
+
}
|
|
1131
|
+
type SubmitAssessmentItemInput = SubmitReviewAssessmentItemInput | SubmitDiagnosticAssessmentItemInput;
|
|
1132
|
+
interface SubmitReviewAssessmentItemResult {
|
|
1133
|
+
attemptId: string;
|
|
1134
|
+
responseVersion: number;
|
|
1135
|
+
status: 'in_progress';
|
|
1136
|
+
responses: AssessmentResponses;
|
|
1137
|
+
/** Canonical ledger after commit or replay; replace local state with it. */
|
|
1138
|
+
itemSubmissions: AssessmentItemSubmission[];
|
|
1139
|
+
submission: AssessmentItemSubmission;
|
|
1140
|
+
transcript: AssessmentTranscript;
|
|
1141
|
+
}
|
|
1142
|
+
/** Safe acknowledgement for one immutable diagnostic item commitment. */
|
|
1143
|
+
interface DiagnosticAssessmentItemReceipt {
|
|
1144
|
+
submissionId: string;
|
|
1145
|
+
routingNodeKey: string;
|
|
1146
|
+
itemIdentifier: string;
|
|
1147
|
+
submittedAt: string;
|
|
1148
|
+
responseVersion: number;
|
|
1149
|
+
answered: boolean;
|
|
1150
|
+
score: AssessmentScore;
|
|
1151
|
+
isCorrect: boolean;
|
|
1152
|
+
}
|
|
1153
|
+
/** Canonical post-commit routing state plus trusted feedback for this administration. */
|
|
1154
|
+
interface SubmitDiagnosticAssessmentItemResult {
|
|
1155
|
+
attemptId: string;
|
|
1156
|
+
responseVersion: number;
|
|
1157
|
+
status: 'in_progress';
|
|
1158
|
+
routing: DiagnosticRoutingSnapshot;
|
|
1159
|
+
submission: DiagnosticAssessmentItemReceipt;
|
|
1160
|
+
transcript: AssessmentTranscript;
|
|
1161
|
+
}
|
|
1162
|
+
type SubmitAssessmentItemResult = SubmitReviewAssessmentItemResult | SubmitDiagnosticAssessmentItemResult;
|
|
1163
|
+
interface SubmitAssessmentInput {
|
|
1164
|
+
expectedResponseVersion: number;
|
|
1165
|
+
submissionId: string;
|
|
1166
|
+
transcript: AssessmentTranscript;
|
|
1167
|
+
}
|
|
1168
|
+
/** The optional mastery portion of a finalize award; at most one field is set. */
|
|
1169
|
+
interface AssessmentMasteryAward {
|
|
1170
|
+
/**
|
|
1171
|
+
* Incremental learning units mastered by this attempt. Cannot be combined
|
|
1172
|
+
* with masteredUnitsAbsolute. The platform bounds the delta against the
|
|
1173
|
+
* course's configured masterableUnits and returns any warning.
|
|
1174
|
+
*/
|
|
1175
|
+
masteredUnits?: number;
|
|
1176
|
+
/**
|
|
1177
|
+
* Absolute mastered-unit total after this attempt; the platform computes
|
|
1178
|
+
* the delta. Cannot be combined with masteredUnits.
|
|
1179
|
+
*/
|
|
1180
|
+
masteredUnitsAbsolute?: number;
|
|
1181
|
+
}
|
|
1182
|
+
interface FinalizeAssessmentInput extends AssessmentMasteryAward {
|
|
1183
|
+
submissionId: string;
|
|
1184
|
+
/** Explicit zero is an award; omission is never interpreted as zero. */
|
|
1185
|
+
xpAwarded: number;
|
|
1186
|
+
}
|
|
1187
|
+
/** Final unpersisted window from the SDK's shared activity-session clock. */
|
|
1188
|
+
interface AssessmentSessionTiming {
|
|
1189
|
+
/** Stable identity for the whole assessment attempt. */
|
|
1190
|
+
runId: string;
|
|
1191
|
+
/** Identity for this individual sitting of a resumable attempt. */
|
|
1192
|
+
resumeId: string;
|
|
1193
|
+
activeSeconds: number;
|
|
1194
|
+
inactiveSeconds?: number;
|
|
1195
|
+
}
|
|
1196
|
+
/** Internal game-backend wire input after the browser SDK closes its clock. */
|
|
1197
|
+
interface SubmitAssessmentRequest extends SubmitAssessmentInput {
|
|
1198
|
+
session?: AssessmentSessionTiming;
|
|
1199
|
+
}
|
|
8
1200
|
|
|
9
1201
|
/**
|
|
10
1202
|
* @fileoverview Server SDK Type Definitions
|
|
@@ -298,6 +1490,87 @@ interface WorkerDeploymentBundle {
|
|
|
298
1490
|
compatibilityFlags?: string[];
|
|
299
1491
|
}
|
|
300
1492
|
|
|
1493
|
+
/**
|
|
1494
|
+
* User Types
|
|
1495
|
+
*
|
|
1496
|
+
* Enums, DTOs and API response types. Database row types are in @playcademy/data/types.
|
|
1497
|
+
*
|
|
1498
|
+
* @module types/user
|
|
1499
|
+
*/
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* OpenID Connect UserInfo claims (NOT a database row).
|
|
1503
|
+
*/
|
|
1504
|
+
interface UserInfo {
|
|
1505
|
+
sub: string;
|
|
1506
|
+
email: string;
|
|
1507
|
+
name: string | null;
|
|
1508
|
+
email_verified?: boolean;
|
|
1509
|
+
given_name?: string;
|
|
1510
|
+
family_name?: string;
|
|
1511
|
+
issuer?: string;
|
|
1512
|
+
lti_roles?: unknown;
|
|
1513
|
+
lti_context?: unknown;
|
|
1514
|
+
lti_resource_link?: unknown;
|
|
1515
|
+
timeback_id?: string;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* Skill Status Types
|
|
1520
|
+
*
|
|
1521
|
+
* Contract for cross-game skill status queries. An answering game exports
|
|
1522
|
+
* `getSkillStatus` from `server/lib/skills.ts`; the platform calls its
|
|
1523
|
+
* reserved `/__playcademy/skills/status` route on behalf of another game.
|
|
1524
|
+
*
|
|
1525
|
+
* @module types/skills
|
|
1526
|
+
*/
|
|
1527
|
+
|
|
1528
|
+
/**
|
|
1529
|
+
* Current status of one skill for one student, as judged by the answering game.
|
|
1530
|
+
*
|
|
1531
|
+
* - `locked`: the student can't work on the skill yet in this game.
|
|
1532
|
+
* - `unlocked`: available to the student, but no progress yet.
|
|
1533
|
+
* - `in_progress`: started, not mastered.
|
|
1534
|
+
* - `mastered`: currently meets this game's mastery bar, including by placement.
|
|
1535
|
+
* - `unknown_skill`: the skill ID isn't recognized. Never report an unrecognized
|
|
1536
|
+
* skill as `locked`.
|
|
1537
|
+
*/
|
|
1538
|
+
type SkillStatus = (typeof SKILL_STATUSES)[number];
|
|
1539
|
+
/** How a `mastered` status was reached. */
|
|
1540
|
+
type SkillMasterySource = (typeof SKILL_MASTERY_SOURCES)[number];
|
|
1541
|
+
interface SkillStatusResult {
|
|
1542
|
+
/** Skill ID, lowercase. */
|
|
1543
|
+
skill: string;
|
|
1544
|
+
/** Current status. */
|
|
1545
|
+
status: SkillStatus;
|
|
1546
|
+
/** How mastery was reached. Only allowed when `status` is `mastered`. */
|
|
1547
|
+
source?: SkillMasterySource;
|
|
1548
|
+
/** ISO 8601 time of the evidence behind this status. */
|
|
1549
|
+
asOf?: string;
|
|
1550
|
+
}
|
|
1551
|
+
/** Body of `POST /api/skills/status`, sent by the asking game's worker. */
|
|
1552
|
+
interface SkillStatusRequest {
|
|
1553
|
+
/** Slug of the game to ask. */
|
|
1554
|
+
game: string;
|
|
1555
|
+
/** Playcademy user ID of the student. */
|
|
1556
|
+
studentId: string;
|
|
1557
|
+
/** Skill IDs to ask about. Normalized (trimmed, lowercased) and de-duplicated. */
|
|
1558
|
+
skills: string[];
|
|
1559
|
+
}
|
|
1560
|
+
/** Why the answering game couldn't answer. */
|
|
1561
|
+
type SkillStatusUnsupportedReason = 'not_implemented' | 'no_active_deployment' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
1562
|
+
/**
|
|
1563
|
+
* What the asking game gets back. When `supported` is false, fall back to
|
|
1564
|
+
* the game's own default (usually: treat the skills as not mastered).
|
|
1565
|
+
*/
|
|
1566
|
+
type SkillStatusLookupResult = {
|
|
1567
|
+
supported: true;
|
|
1568
|
+
results: SkillStatusResult[];
|
|
1569
|
+
} | {
|
|
1570
|
+
supported: false;
|
|
1571
|
+
reason: SkillStatusUnsupportedReason;
|
|
1572
|
+
};
|
|
1573
|
+
|
|
301
1574
|
/**
|
|
302
1575
|
* Server-side Playcademy client for recording student activity to TimeBack.
|
|
303
1576
|
*
|
|
@@ -370,41 +1643,41 @@ declare class PlaycademyClient {
|
|
|
370
1643
|
/** TimeBack integration methods (endActivity) */
|
|
371
1644
|
timeback: {
|
|
372
1645
|
assessments: {
|
|
373
|
-
prepare: (studentId: string, input:
|
|
374
|
-
start: (studentId: string, input:
|
|
375
|
-
latest: (studentId: string, options:
|
|
376
|
-
get: (studentId: string, attemptId: string) => Promise<
|
|
377
|
-
save: (studentId: string, attemptId: string, input:
|
|
378
|
-
resume: (studentId: string, attemptId: string, input?:
|
|
379
|
-
recordProgress: (studentId: string, attemptId: string, input:
|
|
1646
|
+
prepare: (studentId: string, input: StartAssessmentInput) => Promise<AssessmentPreparationResult>;
|
|
1647
|
+
start: (studentId: string, input: StartAssessmentInput, options?: StartAssessmentOptions) => Promise<AssessmentAttemptSnapshot>;
|
|
1648
|
+
latest: (studentId: string, options: GetLatestAssessmentOptions) => Promise<LatestAssessmentResult | null>;
|
|
1649
|
+
get: (studentId: string, attemptId: string) => Promise<AssessmentAttemptSnapshot>;
|
|
1650
|
+
save: (studentId: string, attemptId: string, input: SaveAssessmentInput) => Promise<AssessmentSaveResult>;
|
|
1651
|
+
resume: (studentId: string, attemptId: string, input?: ResumeAssessmentInput) => Promise<AssessmentAttemptSnapshot>;
|
|
1652
|
+
recordProgress: (studentId: string, attemptId: string, input: RecordAssessmentProgressInput) => Promise<{
|
|
380
1653
|
attemptId: string;
|
|
381
1654
|
}>;
|
|
382
|
-
submitItem: (studentId: string, attemptId: string, input:
|
|
383
|
-
submit: (studentId: string, attemptId: string, input:
|
|
1655
|
+
submitItem: (studentId: string, attemptId: string, input: SubmitAssessmentItemInput) => Promise<SubmitAssessmentItemResult>;
|
|
1656
|
+
submit: (studentId: string, attemptId: string, input: SubmitAssessmentRequest, context: {
|
|
384
1657
|
sensorUrl: string;
|
|
385
|
-
}) => Promise<
|
|
386
|
-
finalize: (studentId: string, attemptId: string, input:
|
|
1658
|
+
}) => Promise<AssessmentSubmitResult>;
|
|
1659
|
+
finalize: (studentId: string, attemptId: string, input: FinalizeAssessmentInput, context: {
|
|
387
1660
|
sensorUrl: string;
|
|
388
|
-
}) => Promise<
|
|
1661
|
+
}) => Promise<AssessmentFinalizeResult>;
|
|
389
1662
|
};
|
|
390
|
-
endActivity: (studentId: string, payload:
|
|
1663
|
+
endActivity: (studentId: string, payload: EndActivityPayload) => Promise<EndActivityResponse>;
|
|
391
1664
|
getStudentXp: (studentId: string, options?: {
|
|
392
1665
|
grade?: number;
|
|
393
1666
|
subject?: string;
|
|
394
1667
|
include?: ('perCourse' | 'today')[];
|
|
395
|
-
}) => Promise<
|
|
1668
|
+
}) => Promise<StudentXpResponse>;
|
|
396
1669
|
getStudentMastery: (studentId: string, options?: {
|
|
397
1670
|
grade?: number;
|
|
398
1671
|
subject?: string;
|
|
399
1672
|
include?: 'perCourse'[];
|
|
400
|
-
}) => Promise<
|
|
1673
|
+
}) => Promise<StudentMasteryResponse>;
|
|
401
1674
|
getStudentHighestGradeMastered: (studentId: string, options: {
|
|
402
1675
|
subject: string;
|
|
403
|
-
}) => Promise<
|
|
1676
|
+
}) => Promise<StudentHighestGradeMasteredResponse>;
|
|
404
1677
|
};
|
|
405
1678
|
/** Cross-game skill status (getStatus) */
|
|
406
1679
|
skills: {
|
|
407
|
-
getStatus: (request:
|
|
1680
|
+
getStatus: (request: SkillStatusRequest) => Promise<SkillStatusLookupResult>;
|
|
408
1681
|
};
|
|
409
1682
|
}
|
|
410
1683
|
|
|
@@ -470,4 +1743,4 @@ declare function verifyGameToken(gameToken: string, options?: {
|
|
|
470
1743
|
}): Promise<VerifyGameTokenResponse>;
|
|
471
1744
|
|
|
472
1745
|
export { PlaycademyClient, verifyGameToken };
|
|
473
|
-
export type { IntegrationsConfig, PlaycademyConfig, PlaycademyServerClientConfig, PlaycademyServerClientState, QueueConfig, RequestContext, TimebackBaseConfig, TimebackCourseConfigWithOverrides, TimebackIntegrationConfig, WorkerDeploymentBundle, WorkerResourceBindings };
|
|
1746
|
+
export type { ActivityData, ComponentConfig, ComponentResourceConfig, EndActivityPayload, IntegrationsConfig, OrganizationConfig, PlaycademyConfig, PlaycademyServerClientConfig, PlaycademyServerClientState, QueueConfig, RequestContext, ResourceConfig, TimebackBaseConfig, TimebackCourseConfigWithOverrides, TimebackGrade, TimebackIntegrationConfig, TimebackSubject, UserInfo, WorkerDeploymentBundle, WorkerResourceBindings };
|