@playcademy/sdk 0.24.0 → 0.24.1-beta.2
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 +170 -127
- package/dist/index.js +20 -3
- package/dist/internal.d.ts +2041 -1970
- package/dist/internal.js +22 -3
- package/dist/server/edge.d.ts +24 -1
- package/dist/server/edge.js +1 -1
- package/dist/server.d.ts +24 -1
- package/dist/server.js +1 -1
- package/dist/types.d.ts +458 -411
- package/package.json +1 -1
package/dist/internal.d.ts
CHANGED
|
@@ -81,1950 +81,1234 @@ declare const ASSESSMENT_PURPOSES: readonly ['end_of_course', 'diagnostic', 'rev
|
|
|
81
81
|
declare const PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION: 2;
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
84
|
+
* TimeBack Enums & Literal Types
|
|
85
85
|
*
|
|
86
|
-
*
|
|
86
|
+
* Basic type definitions used throughout the TimeBack integration. Unions
|
|
87
|
+
* derive from the canonical value lists in @playcademy/constants (type-only
|
|
88
|
+
* imports, so this package ships no runtime code).
|
|
87
89
|
*
|
|
88
|
-
* @module types/
|
|
90
|
+
* @module types/timeback/types
|
|
89
91
|
*/
|
|
90
92
|
|
|
91
|
-
type UserRoleEnumType = UserRole;
|
|
92
|
-
type DeveloperStatusEnumType = 'none' | 'pending' | 'approved';
|
|
93
|
-
type DeveloperStatusValue = DeveloperStatusEnumType;
|
|
94
|
-
type TimebackUserRole = 'administrator' | 'aide' | 'guardian' | 'parent' | 'proctor' | 'relative' | 'student' | 'teacher';
|
|
95
|
-
type TimebackOrgType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
96
|
-
interface UserEnrollment {
|
|
97
|
-
gameId?: string;
|
|
98
|
-
courseId: string;
|
|
99
|
-
id?: string;
|
|
100
|
-
grade: number;
|
|
101
|
-
subject: string;
|
|
102
|
-
orgId?: string;
|
|
103
|
-
}
|
|
104
|
-
interface UserOrganization {
|
|
105
|
-
id: string;
|
|
106
|
-
name: string | null;
|
|
107
|
-
type: TimebackOrgType | string;
|
|
108
|
-
isPrimary: boolean;
|
|
109
|
-
}
|
|
110
|
-
interface TimebackStudentProfile {
|
|
111
|
-
role: TimebackUserRole;
|
|
112
|
-
organizations: UserOrganization[];
|
|
113
|
-
}
|
|
114
93
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
94
|
+
* The TimeBack subject vocabulary. 'None' is canonical: TimeBack accepts it
|
|
95
|
+
* on courses and uses it as the fallback for unknown or missing subjects.
|
|
117
96
|
*/
|
|
118
|
-
|
|
119
|
-
id: string;
|
|
120
|
-
}
|
|
121
|
-
interface UserTimebackData extends UserTimebackProfile {
|
|
122
|
-
enrollments: UserEnrollment[];
|
|
123
|
-
}
|
|
124
|
-
type LocalDaySource = 'browser_last_seen' | 'platform_default';
|
|
125
|
-
interface LocalDayContext {
|
|
126
|
-
timeZone: string;
|
|
127
|
-
source: LocalDaySource;
|
|
128
|
-
/** ISO 8601 timestamp for the last persisted timezone observation. */
|
|
129
|
-
observedAt: string | null;
|
|
130
|
-
}
|
|
97
|
+
type TimebackSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
131
98
|
/**
|
|
132
|
-
*
|
|
99
|
+
* Grade levels per AE OneRoster GradeEnum.
|
|
100
|
+
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
133
101
|
*/
|
|
134
|
-
|
|
135
|
-
sub: string;
|
|
136
|
-
email: string;
|
|
137
|
-
name: string | null;
|
|
138
|
-
email_verified?: boolean;
|
|
139
|
-
given_name?: string;
|
|
140
|
-
family_name?: string;
|
|
141
|
-
issuer?: string;
|
|
142
|
-
lti_roles?: unknown;
|
|
143
|
-
lti_context?: unknown;
|
|
144
|
-
lti_resource_link?: unknown;
|
|
145
|
-
timeback_id?: string;
|
|
146
|
-
}
|
|
147
|
-
interface DeveloperStatusResponse {
|
|
148
|
-
status: DeveloperStatusEnumType;
|
|
149
|
-
}
|
|
102
|
+
type TimebackGrade = (typeof TIMEBACK_GRADES)[number];
|
|
150
103
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
104
|
+
* Platform pedagogy levels for a single lesson of instruction.
|
|
105
|
+
* E1 teaches, E2/E3 practice and remediate, E4 is the mastery check.
|
|
153
106
|
*/
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
timeback?: UserTimebackProfile;
|
|
168
|
-
}
|
|
169
|
-
interface GameUser {
|
|
170
|
-
id: string;
|
|
171
|
-
name: string | null;
|
|
172
|
-
role: UserRoleEnumType;
|
|
173
|
-
username: string | null;
|
|
174
|
-
email: string | null;
|
|
175
|
-
localDay: LocalDayContext;
|
|
176
|
-
timeback?: UserTimebackProfile;
|
|
177
|
-
}
|
|
107
|
+
type ELevel = 'E1' | 'E2' | 'E3' | 'E4';
|
|
108
|
+
/**
|
|
109
|
+
* Valid Caliper subject values. The same vocabulary as OneRoster subjects.
|
|
110
|
+
*/
|
|
111
|
+
type CaliperSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
112
|
+
/**
|
|
113
|
+
* OneRoster organization types.
|
|
114
|
+
*/
|
|
115
|
+
type OrganizationType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
116
|
+
/**
|
|
117
|
+
* Lesson types for PowerPath integration.
|
|
118
|
+
*/
|
|
119
|
+
type LessonType = 'powerpath-100' | 'quiz' | 'test-out' | 'placement' | 'unit-test' | 'alpha-read-article' | null;
|
|
178
120
|
|
|
179
121
|
/**
|
|
180
|
-
*
|
|
122
|
+
* TimeBack Configuration Types
|
|
181
123
|
*
|
|
182
|
-
*
|
|
124
|
+
* Configuration interfaces for Organization, Course, Component,
|
|
125
|
+
* Resource, and complete TimeBack setup.
|
|
183
126
|
*
|
|
184
|
-
* @module types/
|
|
127
|
+
* @module types/timeback/config
|
|
185
128
|
*/
|
|
186
129
|
|
|
187
|
-
type GameType = 'hosted' | 'external';
|
|
188
|
-
type GamePlatform = 'web' | 'godot' | (string & {});
|
|
189
130
|
/**
|
|
190
|
-
*
|
|
191
|
-
* Note: createdAt is a string here because it's parsed from JSON file.
|
|
131
|
+
* Organization configuration for TimeBack (user input - optionals allowed)
|
|
192
132
|
*/
|
|
193
|
-
interface
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
sdk?: string;
|
|
201
|
-
cli?: string;
|
|
202
|
-
vite?: string;
|
|
203
|
-
godotSdk?: string;
|
|
204
|
-
godot?: string;
|
|
205
|
-
}
|
|
206
|
-
interface ManifestV2 {
|
|
207
|
-
version: '2';
|
|
208
|
-
platform: string;
|
|
209
|
-
createdAt: string;
|
|
210
|
-
gameVersion?: string;
|
|
211
|
-
versions: ManifestVersions;
|
|
212
|
-
}
|
|
213
|
-
type GameManifest = ManifestV1 | ManifestV2;
|
|
214
|
-
/** One migration in a migrate-mode deploy payload */
|
|
215
|
-
interface DeployMigration {
|
|
216
|
-
/** Journal tag, e.g. '0042_brave_hulk' */
|
|
217
|
-
tag: string;
|
|
218
|
-
/** Pre-split SQL statements (the CLI splits on drizzle statement-breakpoints) */
|
|
219
|
-
statements: string[];
|
|
220
|
-
/** sha-256 of the normalized migration SQL (sha256-v1) */
|
|
221
|
-
checksum: string;
|
|
222
|
-
}
|
|
223
|
-
/** Push-mode database payload: diff-generated SQL guarded by a baseline CAS */
|
|
224
|
-
interface DeployDatabasePush {
|
|
225
|
-
mode: 'push';
|
|
226
|
-
/** Diff-generated DDL to execute (semicolon-separated statements) */
|
|
227
|
-
sql: string;
|
|
228
|
-
/** Stored schemaHash the diff was computed against; null only for a first deploy */
|
|
229
|
-
baselineHash: string | null;
|
|
230
|
-
/** Drizzle snapshot JSON this deploy moves the schema to */
|
|
231
|
-
nextSnapshot: unknown;
|
|
232
|
-
/** Hash of nextSnapshot, stored as the new push baseline on success */
|
|
233
|
-
nextHash: string;
|
|
234
|
-
/** Explicit consent for destructive statements (DROP TABLE, dropped columns) */
|
|
235
|
-
acceptDataLoss?: boolean;
|
|
236
|
-
}
|
|
237
|
-
/** Migrate-mode database payload: the client's complete migration journal */
|
|
238
|
-
interface DeployDatabaseMigrate {
|
|
239
|
-
mode: 'migrate';
|
|
240
|
-
/** The full journal in order; the server applies whatever the ledger lacks */
|
|
241
|
-
migrations: DeployMigration[];
|
|
133
|
+
interface OrganizationConfig {
|
|
134
|
+
/** Display name for your organization */
|
|
135
|
+
name?: string;
|
|
136
|
+
/** Organization type */
|
|
137
|
+
type?: OrganizationType;
|
|
138
|
+
/** Unique identifier (defaults to Playcademy's org) */
|
|
139
|
+
identifier?: string;
|
|
242
140
|
}
|
|
243
141
|
/**
|
|
244
|
-
*
|
|
245
|
-
* field for adopted games; the server verifies state before executing.
|
|
142
|
+
* Course goals for daily student targets
|
|
246
143
|
*/
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
144
|
+
interface CourseGoals {
|
|
145
|
+
/** Target XP students should earn per day */
|
|
146
|
+
dailyXp?: number;
|
|
147
|
+
/** Target lessons per day */
|
|
148
|
+
dailyLessons?: number;
|
|
149
|
+
/** Target active minutes per day */
|
|
150
|
+
dailyActiveMinutes?: number;
|
|
151
|
+
/** Target accuracy percentage */
|
|
152
|
+
dailyAccuracy?: number;
|
|
153
|
+
/** Target mastered units per day */
|
|
154
|
+
dailyMasteredUnits?: number;
|
|
252
155
|
}
|
|
253
156
|
/**
|
|
254
|
-
*
|
|
255
|
-
* starting state for a game that predates server-side deployment state.
|
|
256
|
-
* Consumed by the deploy itself — an unadopted game gets its claimed ledger
|
|
257
|
-
* rows, snapshot/hash, and live fingerprint recorded before the database
|
|
258
|
-
* step runs; adopted games skip the block.
|
|
157
|
+
* Course metrics and totals
|
|
259
158
|
*/
|
|
260
|
-
interface
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
schemaHash?: string;
|
|
272
|
-
integrationsHash?: string;
|
|
273
|
-
buildHash?: string;
|
|
159
|
+
interface CourseMetrics {
|
|
160
|
+
/** Total XP available in the course */
|
|
161
|
+
totalXp?: number;
|
|
162
|
+
/** Total lessons/activities in the course */
|
|
163
|
+
totalLessons?: number;
|
|
164
|
+
/** Total number of grade levels covered by this course */
|
|
165
|
+
totalGrades?: number;
|
|
166
|
+
/** The type of course (e.g. 'optional', 'hole-filling', 'base') */
|
|
167
|
+
courseType?: 'base' | 'hole-filling' | 'optional' | 'Base' | 'Hole-Filling' | 'Optional';
|
|
168
|
+
/** Indicates whether the course is supplemental content */
|
|
169
|
+
isSupplemental?: boolean;
|
|
274
170
|
}
|
|
275
171
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* Captured by the CLI at deploy time and persisted verbatim on the deploy
|
|
279
|
-
* job, so "which commit is running" is answerable from the platform record.
|
|
280
|
-
* Every field is nullable: a tree with no git, no remote, or a detached HEAD
|
|
281
|
-
* is recorded honestly rather than refused. Recorded per deploy, never
|
|
282
|
-
* pinned to the game — a fork deployed under the same slug shows the fork.
|
|
172
|
+
* Complete course metadata structure
|
|
283
173
|
*/
|
|
284
|
-
interface
|
|
285
|
-
/**
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
|
|
305
|
-
/** Frontend zip hash from game_deployment_state (null when unseeded) */
|
|
306
|
-
buildHash: string | null;
|
|
307
|
-
url: string;
|
|
308
|
-
deployedAt: string;
|
|
174
|
+
interface CourseMetadata {
|
|
175
|
+
/** Define the type of course and priority for the student */
|
|
176
|
+
courseType?: 'base' | 'hole-filling' | 'optional';
|
|
177
|
+
/** Boolean value to determine if a course is supplemental to a base course */
|
|
178
|
+
isSupplemental?: boolean;
|
|
179
|
+
/** Boolean value to determine if a course is custom to an individual student */
|
|
180
|
+
isCustom?: boolean;
|
|
181
|
+
/** Signals whether a course is in production with students */
|
|
182
|
+
publishStatus?: 'draft' | 'testing' | 'published' | 'deactivated';
|
|
183
|
+
/** Whether this course appears in the TimeBack catalog for teachers and parents */
|
|
184
|
+
timebackVisible?: boolean;
|
|
185
|
+
/** Who to contact when issues reported with questions */
|
|
186
|
+
contactEmail?: string;
|
|
187
|
+
/** Primary app identifier */
|
|
188
|
+
primaryApp?: string;
|
|
189
|
+
/** Learning goals for students */
|
|
190
|
+
goals?: CourseGoals;
|
|
191
|
+
/** Course metrics and totals */
|
|
192
|
+
metrics?: CourseMetrics;
|
|
193
|
+
/** Vendor-specific metadata (e.g., AlphaLearn) */
|
|
194
|
+
[key: string]: unknown;
|
|
309
195
|
}
|
|
310
|
-
/**
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Course configuration for TimeBack (user input)
|
|
198
|
+
*/
|
|
199
|
+
interface CourseConfig {
|
|
200
|
+
/** Allocated OneRoster sourcedId (set after creation) */
|
|
201
|
+
sourcedId?: string;
|
|
202
|
+
/** Course title (defaults to game name) */
|
|
203
|
+
title?: string;
|
|
204
|
+
/** Subjects (REQUIRED for TimeBack integration) */
|
|
205
|
+
subjects: TimebackSubject[];
|
|
206
|
+
/** Used when recording progress/sessions if not explicitly specified per event. */
|
|
207
|
+
defaultSubject?: TimebackSubject;
|
|
208
|
+
/** Grade levels (REQUIRED for TimeBack integration) */
|
|
209
|
+
grades: TimebackGrade[];
|
|
210
|
+
/** Short course code (optional, auto-generated) */
|
|
211
|
+
courseCode?: string;
|
|
212
|
+
/** Course level (auto-derived from grades) */
|
|
213
|
+
level?: 'Elementary' | 'Middle' | 'High' | 'AP' | string;
|
|
214
|
+
/** Grading system */
|
|
215
|
+
gradingScheme?: 'STANDARD';
|
|
216
|
+
/** Total XP available in this course (REQUIRED before setup) */
|
|
217
|
+
totalXp?: number | null;
|
|
218
|
+
/** Total masterable units in this course (REQUIRED before setup) */
|
|
219
|
+
masterableUnits?: number | null;
|
|
220
|
+
/** Custom Playcademy metadata */
|
|
221
|
+
metadata?: CourseMetadata;
|
|
314
222
|
}
|
|
315
|
-
/**
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Component configuration for TimeBack (user input)
|
|
225
|
+
*/
|
|
226
|
+
interface ComponentConfig {
|
|
227
|
+
/** Component title (defaults to "{course.title} Activities") */
|
|
228
|
+
title?: string;
|
|
229
|
+
/** Display order */
|
|
230
|
+
sortOrder?: number;
|
|
231
|
+
/** Required prior components */
|
|
232
|
+
prerequisites?: string[];
|
|
233
|
+
/** How prerequisites work */
|
|
234
|
+
prerequisiteCriteria?: 'ALL' | 'ANY';
|
|
327
235
|
}
|
|
328
|
-
/**
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Playcademy-specific resource extensions
|
|
238
|
+
*/
|
|
239
|
+
interface PlaycademyResourceMetadata {
|
|
240
|
+
/** Mastery configuration for tracking discrete learning units */
|
|
241
|
+
mastery?: {
|
|
242
|
+
/** Total number of masterable units in the resource */
|
|
243
|
+
masterableUnits: number;
|
|
244
|
+
/** Type of mastery unit for semantic clarity */
|
|
245
|
+
unitType?: 'level' | 'rank' | 'skill' | 'module';
|
|
246
|
+
};
|
|
335
247
|
}
|
|
336
|
-
/**
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Resource configuration for TimeBack (user input)
|
|
250
|
+
*/
|
|
251
|
+
interface ResourceConfig {
|
|
252
|
+
/** Resource title (defaults to "{course.title} Game") */
|
|
253
|
+
title?: string;
|
|
254
|
+
/** Internal resource ID (auto-generated from package.json) */
|
|
255
|
+
vendorResourceId?: string;
|
|
256
|
+
/** Vendor identifier */
|
|
257
|
+
vendorId?: string;
|
|
258
|
+
/** Application identifier */
|
|
259
|
+
applicationId?: string;
|
|
260
|
+
/** Resource roles */
|
|
261
|
+
roles?: ('primary' | 'secondary')[];
|
|
262
|
+
/** Resource importance */
|
|
263
|
+
importance?: 'primary' | 'secondary';
|
|
264
|
+
/** Interactive resource metadata */
|
|
265
|
+
metadata?: {
|
|
266
|
+
/** Resource type */
|
|
267
|
+
type?: 'interactive';
|
|
268
|
+
/** Launch URL (defaults to Playcademy game URL) */
|
|
269
|
+
launchUrl?: string;
|
|
270
|
+
/** Platform name */
|
|
271
|
+
toolProvider?: string;
|
|
272
|
+
/** Teaching method */
|
|
273
|
+
instructionalMethod?: 'exploratory' | 'direct-instruction';
|
|
274
|
+
/** Subject area */
|
|
275
|
+
subject?: TimebackSubject;
|
|
276
|
+
/** Target grades */
|
|
277
|
+
grades?: TimebackGrade[];
|
|
278
|
+
/** Content language */
|
|
279
|
+
language?: string;
|
|
280
|
+
/** Base XP for completion */
|
|
281
|
+
xp?: number;
|
|
282
|
+
/** Playcademy-specific extensions */
|
|
283
|
+
playcademy?: PlaycademyResourceMetadata;
|
|
284
|
+
};
|
|
350
285
|
}
|
|
351
|
-
/**
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Component Resource link configuration (user input)
|
|
288
|
+
*/
|
|
289
|
+
interface ComponentResourceConfig {
|
|
290
|
+
/** Link title (defaults to "{resource.title} Activity") */
|
|
291
|
+
title?: string;
|
|
292
|
+
/** Display order */
|
|
293
|
+
sortOrder?: number;
|
|
294
|
+
/** Lesson type for PowerPath integration */
|
|
295
|
+
lessonType?: LessonType;
|
|
360
296
|
}
|
|
361
|
-
interface
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
297
|
+
interface TimebackCourseConfig {
|
|
298
|
+
subject: string;
|
|
299
|
+
grade: number;
|
|
300
|
+
}
|
|
301
|
+
interface DerivedPlatformCourseConfig extends TimebackCourseConfig {
|
|
302
|
+
title: string;
|
|
303
|
+
courseCode: string;
|
|
304
|
+
level: string;
|
|
305
|
+
metadata?: Record<string, unknown>;
|
|
306
|
+
totalXp?: number | null;
|
|
307
|
+
masterableUnits?: number | null;
|
|
365
308
|
}
|
|
309
|
+
|
|
366
310
|
/**
|
|
367
|
-
*
|
|
311
|
+
* TimeBack Client SDK DTOs
|
|
368
312
|
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
313
|
+
* Data transfer objects for the TimeBack client SDK including
|
|
314
|
+
* progress tracking, session management, and activity completion.
|
|
315
|
+
*
|
|
316
|
+
* Note: TimebackClientConfig lives in @playcademy/timeback as it's
|
|
317
|
+
* SDK configuration, not a DTO.
|
|
318
|
+
*
|
|
319
|
+
* @module types/timeback/client
|
|
372
320
|
*/
|
|
373
|
-
|
|
374
|
-
gameId: string;
|
|
375
|
-
/** True iff a game_deployment_state row exists for the game */
|
|
376
|
-
seeded: boolean;
|
|
377
|
-
game: DeploymentStateGame | null;
|
|
378
|
-
dashboard: DeploymentStateDashboard | null;
|
|
379
|
-
database: DeploymentStateDatabase;
|
|
380
|
-
secrets: DeploymentStateSecrets;
|
|
381
|
-
/** Hash of the declared integrations config (null when unseeded) */
|
|
382
|
-
integrationsHash: string | null;
|
|
383
|
-
/** Last deployed compatibility date (null when unseeded) */
|
|
384
|
-
compatibilityDate: string | null;
|
|
385
|
-
lastDeploy: DeploymentStateLastDeploy | null;
|
|
386
|
-
}
|
|
321
|
+
|
|
387
322
|
/**
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
* Key names only — HMAC digests never leave the server. `remoteOnlyManaged`
|
|
391
|
-
* keys are the only legal prune candidates; `remoteOnlyUnmanaged` keys were
|
|
392
|
-
* set outside the platform and are untouchable.
|
|
323
|
+
* Known extensions for TimeBack Activity Metrics Collection
|
|
393
324
|
*/
|
|
394
|
-
interface
|
|
395
|
-
/**
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
325
|
+
interface TimebackActivityExtensions {
|
|
326
|
+
/** Percentage complete (0-100) for the app course */
|
|
327
|
+
pctCompleteApp?: number;
|
|
328
|
+
/** Allow other arbitrary extensions */
|
|
329
|
+
[key: string]: unknown;
|
|
330
|
+
}
|
|
331
|
+
interface MasteryWriteWarning {
|
|
332
|
+
code: 'MASTERY_WRITE_CAPPED';
|
|
333
|
+
message: string;
|
|
334
|
+
currentMasteredUnits: number;
|
|
335
|
+
attemptedMasteredUnits: number;
|
|
336
|
+
appliedMasteredUnits: number;
|
|
337
|
+
storedMasteredUnits: number;
|
|
338
|
+
masterableUnits: number;
|
|
405
339
|
}
|
|
406
340
|
/**
|
|
407
|
-
*
|
|
408
|
-
* a game that predates server-side deployment state. Carries the claimed
|
|
409
|
-
* migrate-mode history (tag + journal), the push-mode snapshot, or both.
|
|
341
|
+
* Activity data for ending an activity
|
|
410
342
|
*/
|
|
411
|
-
interface
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
343
|
+
interface ActivityData {
|
|
344
|
+
/** Unique activity identifier (required) */
|
|
345
|
+
activityId: string;
|
|
346
|
+
/** Grade level for this activity (required for multi-grade course routing) */
|
|
347
|
+
grade: number;
|
|
348
|
+
/** Subject area (required for multi-grade course routing) */
|
|
349
|
+
subject: CaliperSubject;
|
|
350
|
+
/** Activity display name (optional) */
|
|
351
|
+
activityName?: string;
|
|
352
|
+
/**
|
|
353
|
+
* Optional course to report against. When omitted, the SDK fills it from
|
|
354
|
+
* the student's enrollment for this grade/subject (preferring a
|
|
355
|
+
* hole-filling course). The platform uses it if it's the base course or one
|
|
356
|
+
* of that grade/subject's hole-filling courses, otherwise the base course.
|
|
424
357
|
*/
|
|
425
|
-
|
|
358
|
+
courseId?: string;
|
|
426
359
|
/**
|
|
427
|
-
*
|
|
428
|
-
*
|
|
360
|
+
* The student's enrollment in `courseId`. Filled by the SDK alongside an
|
|
361
|
+
* automatic `courseId`; recorded on hole-filling Caliper events.
|
|
429
362
|
*/
|
|
430
|
-
|
|
363
|
+
enrollmentId?: string;
|
|
364
|
+
/** Course display name (auto-filled from config if not provided) */
|
|
365
|
+
courseName?: string;
|
|
366
|
+
/** Student email address (optional) */
|
|
367
|
+
studentEmail?: string;
|
|
368
|
+
/** Application name for Caliper events (defaults to 'Game') */
|
|
369
|
+
appName?: string;
|
|
370
|
+
/** Sensor URL for Caliper events (defaults to baseUrl) */
|
|
371
|
+
sensorUrl?: string;
|
|
431
372
|
}
|
|
432
373
|
/**
|
|
433
|
-
*
|
|
434
|
-
* delta). Drops are as load-bearing as creates: an object created by one
|
|
435
|
-
* migration and dropped by a later one must not be demanded of the live
|
|
436
|
-
* database, and its live presence beyond the claim must not convict a
|
|
437
|
-
* migration whose object is already explained by claimed history.
|
|
374
|
+
* Score data for ending an activity
|
|
438
375
|
*/
|
|
439
|
-
interface
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
name: string;
|
|
445
|
-
columns: string[];
|
|
446
|
-
}[];
|
|
447
|
-
addsColumns: {
|
|
448
|
-
table: string;
|
|
449
|
-
column: string;
|
|
450
|
-
}[];
|
|
451
|
-
createsIndexes: string[];
|
|
452
|
-
createsViews: string[];
|
|
453
|
-
dropsTables: string[];
|
|
454
|
-
dropsColumns: {
|
|
455
|
-
table: string;
|
|
456
|
-
column: string;
|
|
457
|
-
}[];
|
|
458
|
-
dropsIndexes: string[];
|
|
459
|
-
dropsViews: string[];
|
|
460
|
-
}
|
|
461
|
-
/** Outcome of the `db realign <tag>` checksum pardon */
|
|
462
|
-
interface MigrationRealignResponse {
|
|
463
|
-
tag: string;
|
|
464
|
-
/** The re-recorded checksum */
|
|
465
|
-
checksum: string;
|
|
466
|
-
/** Algorithm stamped alongside it (e.g. 'sha256-v1') */
|
|
467
|
-
checksumAlgo: string;
|
|
468
|
-
}
|
|
469
|
-
/** How a ledger escape-hatch resolution lands: record the row, or remove it */
|
|
470
|
-
type MigrationResolution = 'applied' | 'rolled-back';
|
|
471
|
-
/** Outcome of the `db resolve <tag>` ledger escape hatch */
|
|
472
|
-
interface MigrationResolveResponse {
|
|
473
|
-
tag: string;
|
|
474
|
-
resolution: MigrationResolution;
|
|
376
|
+
interface EndActivityScoreData {
|
|
377
|
+
/** Number of questions answered correctly */
|
|
378
|
+
correctQuestions: number;
|
|
379
|
+
/** Total number of questions */
|
|
380
|
+
totalQuestions: number;
|
|
475
381
|
/**
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
382
|
+
* XP earned for this activity.
|
|
383
|
+
*
|
|
384
|
+
* Required when the game reports directly (platform, demo, standalone).
|
|
385
|
+
* A child-launched game may omit it: narrow the client with the SDK's
|
|
386
|
+
* `isChildLaunched()` for the relaxed signature. The parent game
|
|
387
|
+
* decides the award, and a value provided here travels only as the
|
|
388
|
+
* `childXpSuggested` audit suggestion.
|
|
479
389
|
*/
|
|
480
|
-
|
|
390
|
+
xpAwarded: number;
|
|
481
391
|
/**
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
392
|
+
* Incremental learning units mastered this session.
|
|
393
|
+
*
|
|
394
|
+
* Cannot be used with masteredUnitsAbsolute. When a course has mastery
|
|
395
|
+
* tracking configured, the platform bounds submissions that would move
|
|
396
|
+
* the student below 0 or above the configured masterableUnits maximum and
|
|
397
|
+
* returns warning metadata.
|
|
485
398
|
*/
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
|
|
498
|
-
/** Client idempotency key (null for legacy clients) */
|
|
499
|
-
deployId: string | null;
|
|
500
|
-
error: string | null;
|
|
501
|
-
}
|
|
502
|
-
/** A Time Travel restore in a game's execution history */
|
|
503
|
-
interface GameDeployHistoryRestore {
|
|
504
|
-
kind: 'restore';
|
|
505
|
-
/** When the restore ran, ISO 8601 */
|
|
506
|
-
at: string;
|
|
507
|
-
/** Who restored: their email, or DELETED_ACCOUNT_LABEL when the account is gone */
|
|
508
|
-
by: string;
|
|
509
|
-
/** The moment the database was rewound to, ISO 8601 */
|
|
510
|
-
restoredTo: string;
|
|
511
|
-
}
|
|
512
|
-
/** A client-gate refusal in a game's execution history */
|
|
513
|
-
interface GameDeployHistoryBlocked {
|
|
514
|
-
kind: 'blocked';
|
|
515
|
-
/** When the gate refused, ISO 8601 */
|
|
516
|
-
at: string;
|
|
517
|
-
/** Whose deploy was refused: their email, or DELETED_ACCOUNT_LABEL */
|
|
518
|
-
by: string;
|
|
519
|
-
/** Stable `blocked:*` code the gate refused with */
|
|
520
|
-
code: string;
|
|
521
|
-
/** The gate's plain-text refusal reason */
|
|
522
|
-
reason: string;
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* One entry in a game's execution history: deploy jobs interleaved with
|
|
526
|
-
* the platform-visible events the job system never sees (restores,
|
|
527
|
-
* client-gate refusals), newest first.
|
|
528
|
-
*/
|
|
529
|
-
type GameDeployHistoryEntry = GameDeployHistoryDeploy | GameDeployHistoryRestore | GameDeployHistoryBlocked;
|
|
530
|
-
/** Deploy history served by GET /api/games/:slug/deployments */
|
|
531
|
-
interface GameDeployHistoryResponse {
|
|
532
|
-
deploys: GameDeployHistoryEntry[];
|
|
399
|
+
masteredUnits?: number;
|
|
400
|
+
/**
|
|
401
|
+
* Absolute total of mastered units.
|
|
402
|
+
*
|
|
403
|
+
* The platform computes the delta from the current value. Cannot be used
|
|
404
|
+
* with masteredUnits. When a course has mastery tracking configured, the
|
|
405
|
+
* platform bounds values outside 0..masterableUnits and returns warning
|
|
406
|
+
* metadata.
|
|
407
|
+
*/
|
|
408
|
+
masteredUnitsAbsolute?: number;
|
|
409
|
+
/** Optional arbitrary extensions to include in the Caliper event */
|
|
410
|
+
extensions?: TimebackActivityExtensions;
|
|
533
411
|
}
|
|
412
|
+
|
|
534
413
|
/**
|
|
535
|
-
*
|
|
536
|
-
* describes it: what is running and which commit produced it.
|
|
414
|
+
* Caliper Protocol Types
|
|
537
415
|
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
416
|
+
* Types for the IMS Caliper Analytics standard used by TimeBack
|
|
417
|
+
* for learning activity tracking.
|
|
418
|
+
*
|
|
419
|
+
* @module types/timeback/caliper
|
|
542
420
|
*/
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
* Git provenance the deploying CLI captured. Null for deploys that
|
|
549
|
-
* predate provenance capture, so the fleet list is complete on day one
|
|
550
|
-
* and a consumer can tell "unknown" from "not in git".
|
|
551
|
-
*/
|
|
552
|
-
source: DeploySource | null;
|
|
553
|
-
/** When the deploy succeeded, ISO 8601 */
|
|
554
|
-
deployedAt: string;
|
|
555
|
-
}
|
|
556
|
-
/** GET /api/releases — the current release of every game */
|
|
557
|
-
interface ReleasesResponse {
|
|
558
|
-
releases: Release[];
|
|
421
|
+
|
|
422
|
+
interface TimebackStoredCaliperMetric {
|
|
423
|
+
type: string;
|
|
424
|
+
value?: number;
|
|
425
|
+
subType?: string;
|
|
559
426
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
427
|
+
interface TimebackStoredCaliperEntity {
|
|
428
|
+
id?: string;
|
|
429
|
+
type?: string;
|
|
430
|
+
name?: string;
|
|
431
|
+
email?: string;
|
|
432
|
+
app?: {
|
|
433
|
+
id?: string;
|
|
434
|
+
name?: string;
|
|
435
|
+
};
|
|
436
|
+
activity?: {
|
|
437
|
+
id?: string;
|
|
438
|
+
name?: string;
|
|
439
|
+
extensions?: Record<string, unknown> | null;
|
|
440
|
+
};
|
|
441
|
+
course?: {
|
|
442
|
+
id?: string;
|
|
443
|
+
name?: string;
|
|
444
|
+
};
|
|
445
|
+
items?: TimebackStoredCaliperMetric[];
|
|
446
|
+
extensions?: Record<string, unknown> | null;
|
|
447
|
+
[key: string]: unknown;
|
|
569
448
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
449
|
+
interface TimebackStoredCaliperEvent {
|
|
450
|
+
id: number;
|
|
451
|
+
externalId: string;
|
|
452
|
+
sensor: string;
|
|
453
|
+
type: string;
|
|
454
|
+
profile?: string;
|
|
455
|
+
action: string;
|
|
456
|
+
eventTime: string;
|
|
457
|
+
sendTime: string;
|
|
458
|
+
updated_at: string | null;
|
|
459
|
+
created_at: string;
|
|
460
|
+
deleted_at: string | null;
|
|
461
|
+
actor: TimebackStoredCaliperEntity;
|
|
462
|
+
object: TimebackStoredCaliperEntity;
|
|
463
|
+
generated?: TimebackStoredCaliperEntity | null;
|
|
464
|
+
target?: TimebackStoredCaliperEntity | null;
|
|
465
|
+
referrer?: TimebackStoredCaliperEntity | null;
|
|
466
|
+
edApp?: TimebackStoredCaliperEntity | null;
|
|
467
|
+
group?: TimebackStoredCaliperEntity | null;
|
|
468
|
+
membership?: TimebackStoredCaliperEntity | null;
|
|
469
|
+
session?: TimebackStoredCaliperEntity | null;
|
|
470
|
+
federatedSession?: TimebackStoredCaliperEntity | null;
|
|
471
|
+
extensions?: Record<string, unknown> | null;
|
|
472
|
+
clientAppId?: string | null;
|
|
576
473
|
}
|
|
474
|
+
|
|
577
475
|
/**
|
|
578
|
-
*
|
|
476
|
+
* Game-side learning metrics for a single student.
|
|
579
477
|
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* belonging to the CURRENT database and within Time Travel's retention
|
|
583
|
-
* window are served — a `db reset` creates a new database (bookmarks do
|
|
584
|
-
* not cross that boundary), and Cloudflare expires bookmarks after the
|
|
585
|
-
* account plan's retention period.
|
|
586
|
-
*/
|
|
587
|
-
interface GameRestorePoint {
|
|
588
|
-
/** Deployment row id — pass to the restore endpoint (or a unique prefix to the CLI) */
|
|
589
|
-
id: string;
|
|
590
|
-
/**
|
|
591
|
-
* When the bookmark was captured (the exact state a restore rewinds
|
|
592
|
-
* to), ISO 8601. Rows from before capture times were recorded fall
|
|
593
|
-
* back to the deploy completion time.
|
|
594
|
-
*/
|
|
595
|
-
capturedAt: string;
|
|
596
|
-
/** Whether the capturing deployment is the currently active one */
|
|
597
|
-
active: boolean;
|
|
598
|
-
}
|
|
599
|
-
/** Restore points served by GET /api/games/:slug/database/restore-points */
|
|
600
|
-
interface GameRestorePointsResponse {
|
|
601
|
-
restorePoints: GameRestorePoint[];
|
|
602
|
-
/**
|
|
603
|
-
* True for push-adopted games, whose recorded snapshot cannot rewind
|
|
604
|
-
* with the data: restore is categorically refused, so the list is
|
|
605
|
-
* served empty rather than offering points that can never restore.
|
|
606
|
-
*/
|
|
607
|
-
restoreUnsupported: boolean;
|
|
608
|
-
}
|
|
609
|
-
/** Outcome of a Time Travel restore (PLA-217 D10) */
|
|
610
|
-
interface DatabaseRestoreResponse {
|
|
611
|
-
restorePointId: string;
|
|
612
|
-
/** The moment the database was rewound to (the bookmark's capture time), ISO 8601 */
|
|
613
|
-
restoredTo: string;
|
|
614
|
-
/**
|
|
615
|
-
* Live schema fingerprint captured after the restore: the developer's
|
|
616
|
-
* audited attestation that the rewound state is intended, so the next
|
|
617
|
-
* deploy verifies against it.
|
|
618
|
-
*/
|
|
619
|
-
schemaFingerprint: string;
|
|
620
|
-
/** False when the game has no deployment-state row to re-attest */
|
|
621
|
-
fingerprintRecorded: boolean;
|
|
622
|
-
/**
|
|
623
|
-
* Bookmark of the database as it was just before this restore — the
|
|
624
|
-
* undo handle. Null only if Cloudflare omits it from the response.
|
|
625
|
-
*/
|
|
626
|
-
previousBookmark: string | null;
|
|
627
|
-
}
|
|
628
|
-
/** Push-mode reset payload: the full schema that rebuilds the fresh database */
|
|
629
|
-
interface DatabaseResetPush {
|
|
630
|
-
mode: 'push';
|
|
631
|
-
/** Full schema DDL (semicolon-separated statements) */
|
|
632
|
-
sql: string;
|
|
633
|
-
/** Drizzle snapshot JSON, stored as the new push baseline */
|
|
634
|
-
nextSnapshot: unknown;
|
|
635
|
-
/** Hash of nextSnapshot, stored as the new schemaHash */
|
|
636
|
-
nextHash: string;
|
|
637
|
-
}
|
|
638
|
-
/** Migrate-mode reset payload: the complete journal to replay on the fresh database */
|
|
639
|
-
interface DatabaseResetMigrate {
|
|
640
|
-
mode: 'migrate';
|
|
641
|
-
/** The full journal in order; replayed one-by-one, rebuilding the ledger */
|
|
642
|
-
migrations: DeployMigration[];
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Mode-aware reset payload (PLA-217 D2). Reset recreates the database, so
|
|
646
|
-
* the ledger dies with it and there is no baseline to compare against —
|
|
647
|
-
* adopted games must send how to rebuild.
|
|
478
|
+
* Returned by opted-in game workers from their reserved
|
|
479
|
+
* `/__playcademy/metrics` route.
|
|
648
480
|
*/
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
/** SQL schema statements (DDL: CREATE, ALTER, etc.) */
|
|
657
|
-
sql: string;
|
|
658
|
-
/** Hash of the schema for change detection */
|
|
659
|
-
hash: string;
|
|
660
|
-
};
|
|
661
|
-
/** Mode-aware rebuild payload (mandatory for adopted games) */
|
|
662
|
-
database?: DatabaseResetDatabasePayload;
|
|
481
|
+
interface GameMetricsResponse {
|
|
482
|
+
/** Student identifier (Playcademy user ID). */
|
|
483
|
+
studentId: string;
|
|
484
|
+
/** Student email, when available to the game. */
|
|
485
|
+
email?: string;
|
|
486
|
+
/** Per-course metrics grouped by TimeBack grade and subject. */
|
|
487
|
+
courses: GameCourseMetrics[];
|
|
663
488
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
489
|
+
interface GameCourseMetrics {
|
|
490
|
+
/** Grade level matching the game's TimeBack integration. */
|
|
491
|
+
grade: TimebackGrade;
|
|
492
|
+
/** Subject matching the game's TimeBack integration. */
|
|
493
|
+
subject: TimebackSubject;
|
|
494
|
+
/** Total XP the game has recorded for this student in this course. */
|
|
495
|
+
totalXp?: number;
|
|
496
|
+
/** Mastered units the game has recorded for this course. */
|
|
497
|
+
masteredUnits?: number;
|
|
498
|
+
/** Total active time in seconds the game has recorded for this course. */
|
|
499
|
+
activeTimeSeconds?: number;
|
|
500
|
+
/** Optional per-run breakdown keyed by Timeback run ID. */
|
|
501
|
+
activities?: GameRunMetrics[];
|
|
672
502
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
/** Specific constraint type */
|
|
682
|
-
constraintType?: 'UNIQUE' | 'FOREIGN_KEY' | 'NOT_NULL';
|
|
683
|
-
/** Token near syntax error */
|
|
684
|
-
nearToken?: string;
|
|
685
|
-
/** Specific error type within category */
|
|
686
|
-
errorType?: 'TABLE_NOT_FOUND' | 'SYNTAX_ERROR';
|
|
503
|
+
interface GameRunMetrics {
|
|
504
|
+
runId: string;
|
|
505
|
+
activityId: string;
|
|
506
|
+
activityName?: string;
|
|
507
|
+
totalXp?: number;
|
|
508
|
+
masteredUnits?: number;
|
|
509
|
+
activeTimeSeconds?: number;
|
|
510
|
+
score?: number;
|
|
687
511
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
stack?: string;
|
|
708
|
-
/** Structured error details if seed failed */
|
|
709
|
-
details?: SeedErrorDetails;
|
|
512
|
+
type GameMetricsUnsupportedReason = 'no_timeback_integration' | 'no_active_deployment' | 'route_not_implemented' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
513
|
+
type GameMetricsProxyResponse = {
|
|
514
|
+
supported: true;
|
|
515
|
+
metrics: GameMetricsResponse;
|
|
516
|
+
} | {
|
|
517
|
+
supported: false;
|
|
518
|
+
reason: GameMetricsUnsupportedReason;
|
|
519
|
+
details?: string;
|
|
520
|
+
};
|
|
521
|
+
type GameMetricComparisonMetric = 'xp' | 'mastery' | 'time' | 'score';
|
|
522
|
+
type GameMetricComparisonKind = 'number' | 'time' | 'percent';
|
|
523
|
+
type GameMetricComparisonRowStatus = 'matched' | 'discrepant' | 'not_reported_by_game' | 'not_recorded_by_timeback';
|
|
524
|
+
interface GameMetricComparisonRow {
|
|
525
|
+
metric: GameMetricComparisonMetric;
|
|
526
|
+
kind: GameMetricComparisonKind;
|
|
527
|
+
status: GameMetricComparisonRowStatus;
|
|
528
|
+
timebackValue?: number;
|
|
529
|
+
gameValue?: number;
|
|
530
|
+
delta?: number;
|
|
710
531
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}[];
|
|
532
|
+
type GameRunMetricsComparisonStatus = 'matched' | 'discrepant' | 'not_reported' | 'unavailable';
|
|
533
|
+
interface GameRunMetricsComparisonSummary {
|
|
534
|
+
runId: string;
|
|
535
|
+
status: GameRunMetricsComparisonStatus;
|
|
536
|
+
discrepancyCount: number;
|
|
537
|
+
reason?: GameMetricsUnsupportedReason;
|
|
538
|
+
}
|
|
539
|
+
interface GameRunMetricsComparison extends GameRunMetricsComparisonSummary {
|
|
540
|
+
rows: GameMetricComparisonRow[];
|
|
721
541
|
}
|
|
722
542
|
|
|
723
543
|
/**
|
|
724
|
-
*
|
|
544
|
+
* QTI API Response Types
|
|
725
545
|
*
|
|
726
|
-
*
|
|
546
|
+
* Types for the QTI assessment content service responses.
|
|
547
|
+
* These mirror the JSON structure returned by the QTI API
|
|
548
|
+
* (beyond-qti-2) for assessment tests and items.
|
|
549
|
+
*
|
|
550
|
+
* @module types/timeback/qti
|
|
727
551
|
*/
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
offset?: number;
|
|
733
|
-
gameId?: string;
|
|
734
|
-
}
|
|
735
|
-
interface LeaderboardEntry {
|
|
736
|
-
rank: number;
|
|
737
|
-
userId: string;
|
|
738
|
-
username: string;
|
|
739
|
-
userImage?: string | null;
|
|
740
|
-
score: number;
|
|
741
|
-
achievedAt: Date;
|
|
742
|
-
metadata?: Record<string, unknown>;
|
|
743
|
-
gameId?: string;
|
|
744
|
-
gameTitle?: string;
|
|
745
|
-
gameSlug?: string;
|
|
552
|
+
interface QtiItemRef {
|
|
553
|
+
identifier: string;
|
|
554
|
+
href: string;
|
|
555
|
+
sequence?: number;
|
|
746
556
|
}
|
|
747
|
-
interface
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
557
|
+
interface QtiAssessmentSection {
|
|
558
|
+
identifier: string;
|
|
559
|
+
title: string;
|
|
560
|
+
visible?: boolean;
|
|
561
|
+
required?: boolean;
|
|
562
|
+
fixed?: boolean;
|
|
563
|
+
sequence?: number;
|
|
564
|
+
'qti-assessment-item-ref'?: QtiItemRef[];
|
|
752
565
|
}
|
|
753
|
-
interface
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
566
|
+
interface QtiTestPart {
|
|
567
|
+
identifier: string;
|
|
568
|
+
navigationMode: 'linear' | 'nonlinear';
|
|
569
|
+
submissionMode: 'individual' | 'simultaneous';
|
|
570
|
+
'qti-assessment-section': QtiAssessmentSection[];
|
|
757
571
|
}
|
|
758
|
-
interface
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
572
|
+
interface QtiOutcomeDeclaration {
|
|
573
|
+
identifier: string;
|
|
574
|
+
cardinality?: 'single' | 'multiple' | 'ordered' | 'record';
|
|
575
|
+
baseType: 'boolean' | 'directedPair' | 'duration' | 'file' | 'float' | 'identifier' | 'integer' | 'pair' | 'point' | 'string' | 'uri';
|
|
576
|
+
normalMaximum?: number;
|
|
577
|
+
normalMinimum?: number;
|
|
578
|
+
defaultValue?: {
|
|
579
|
+
value?: unknown;
|
|
580
|
+
};
|
|
766
581
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
score: number;
|
|
777
|
-
achievedAt: Date;
|
|
582
|
+
interface QtiAssessmentTest {
|
|
583
|
+
identifier: string;
|
|
584
|
+
title: string;
|
|
585
|
+
qtiVersion?: string;
|
|
586
|
+
timeLimit?: number;
|
|
587
|
+
maxAttempts?: number;
|
|
588
|
+
toolsEnabled?: Record<string, boolean>;
|
|
589
|
+
'qti-test-part': QtiTestPart[];
|
|
590
|
+
'qti-outcome-declaration'?: QtiOutcomeDeclaration[];
|
|
778
591
|
metadata?: Record<string, unknown>;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
592
|
+
rawXml?: string;
|
|
593
|
+
content?: Record<string, unknown>;
|
|
594
|
+
createdAt?: string;
|
|
595
|
+
updatedAt?: string;
|
|
596
|
+
}
|
|
597
|
+
interface QtiAssessmentTestListResponse {
|
|
598
|
+
items: QtiAssessmentTest[];
|
|
599
|
+
total: number;
|
|
600
|
+
page: number;
|
|
601
|
+
pages: number;
|
|
602
|
+
limit: number;
|
|
603
|
+
sort: string;
|
|
604
|
+
order: 'asc' | 'desc';
|
|
605
|
+
}
|
|
606
|
+
interface QtiResponseDeclaration {
|
|
607
|
+
identifier: string;
|
|
608
|
+
cardinality: 'single' | 'multiple' | 'ordered';
|
|
609
|
+
baseType: string;
|
|
610
|
+
correctResponse?: {
|
|
611
|
+
value: string[];
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
interface QtiAssessmentItem {
|
|
615
|
+
identifier: string;
|
|
616
|
+
title: string;
|
|
617
|
+
type?: string;
|
|
618
|
+
qtiVersion?: string;
|
|
619
|
+
timeDependent?: boolean;
|
|
620
|
+
adaptive?: boolean;
|
|
621
|
+
preInteraction?: string;
|
|
622
|
+
interaction?: Record<string, unknown>;
|
|
623
|
+
postInteraction?: string;
|
|
624
|
+
responseDeclarations?: QtiResponseDeclaration[];
|
|
625
|
+
outcomeDeclarations?: QtiOutcomeDeclaration[];
|
|
626
|
+
responseProcessing?: Record<string, unknown>;
|
|
627
|
+
metadata?: Record<string, unknown>;
|
|
628
|
+
modalFeedback?: unknown[];
|
|
629
|
+
feedbackInline?: unknown[];
|
|
630
|
+
feedbackBlock?: unknown[];
|
|
631
|
+
rubrics?: unknown[];
|
|
632
|
+
stimulus?: unknown;
|
|
633
|
+
rawXml?: string;
|
|
634
|
+
content?: Record<string, unknown>;
|
|
635
|
+
createdAt?: string;
|
|
636
|
+
updatedAt?: string;
|
|
637
|
+
}
|
|
638
|
+
interface QtiAssessmentItemListResponse {
|
|
639
|
+
items: QtiAssessmentItem[];
|
|
640
|
+
total: number;
|
|
641
|
+
page: number;
|
|
642
|
+
pages: number;
|
|
643
|
+
limit: number;
|
|
644
|
+
sort: string;
|
|
645
|
+
order: 'asc' | 'desc';
|
|
646
|
+
}
|
|
647
|
+
/** Request an exact, independently owned copy through the normal question collection endpoint. */
|
|
648
|
+
interface QtiQuestionCopyCreateInput {
|
|
649
|
+
mode: 'copy';
|
|
650
|
+
sourceItemIdentifier: string;
|
|
651
|
+
}
|
|
652
|
+
type QtiQuestionCreateInput = QtiQuestionCopyCreateInput | Record<string, unknown>;
|
|
653
|
+
interface QtiTestQuestionRef {
|
|
654
|
+
ownership?: 'owned' | 'shared';
|
|
655
|
+
reference: {
|
|
656
|
+
identifier: string;
|
|
657
|
+
href: string;
|
|
658
|
+
testPart: string;
|
|
659
|
+
section: string;
|
|
660
|
+
};
|
|
661
|
+
question: QtiAssessmentItem;
|
|
662
|
+
}
|
|
663
|
+
interface QtiTestQuestionsResponse {
|
|
664
|
+
assessmentTest: string;
|
|
665
|
+
title: string;
|
|
666
|
+
totalQuestions: number;
|
|
667
|
+
questions: QtiTestQuestionRef[];
|
|
782
668
|
}
|
|
783
669
|
|
|
784
670
|
/**
|
|
785
|
-
*
|
|
671
|
+
* TimeBack API Request/Response Types
|
|
786
672
|
*
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
* reserved `/__playcademy/skills/status` route on behalf of another game.
|
|
673
|
+
* Types for TimeBack API endpoints including XP tracking,
|
|
674
|
+
* setup, verification, and activity completion.
|
|
790
675
|
*
|
|
791
|
-
* @module types/
|
|
676
|
+
* @module types/timeback/api
|
|
792
677
|
*/
|
|
793
678
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
* - `locked`: the student can't work on the skill yet in this game.
|
|
798
|
-
* - `unlocked`: available to the student, but no progress yet.
|
|
799
|
-
* - `in_progress`: started, not mastered.
|
|
800
|
-
* - `mastered`: currently meets this game's mastery bar, including by placement.
|
|
801
|
-
* - `unknown_skill`: the skill ID isn't recognized. Never report an unrecognized
|
|
802
|
-
* skill as `locked`.
|
|
803
|
-
*/
|
|
804
|
-
type SkillStatus = (typeof SKILL_STATUSES)[number];
|
|
805
|
-
/** How a `mastered` status was reached. */
|
|
806
|
-
type SkillMasterySource = (typeof SKILL_MASTERY_SOURCES)[number];
|
|
807
|
-
interface SkillStatusResult {
|
|
808
|
-
/** Skill ID, lowercase. */
|
|
809
|
-
skill: string;
|
|
810
|
-
/** Current status. */
|
|
811
|
-
status: SkillStatus;
|
|
812
|
-
/** How mastery was reached. Only allowed when `status` is `mastered`. */
|
|
813
|
-
source?: SkillMasterySource;
|
|
814
|
-
/** ISO 8601 time of the evidence behind this status. */
|
|
815
|
-
asOf?: string;
|
|
679
|
+
interface PopulateStudentResponse {
|
|
680
|
+
status: string;
|
|
681
|
+
message?: string;
|
|
816
682
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
683
|
+
interface TimebackSetupRequest {
|
|
684
|
+
gameId: string;
|
|
685
|
+
config: {
|
|
686
|
+
organization: {
|
|
687
|
+
name: string;
|
|
688
|
+
type: string;
|
|
689
|
+
identifier: string;
|
|
690
|
+
};
|
|
691
|
+
course: {
|
|
692
|
+
/**
|
|
693
|
+
* Client-chosen OneRoster sourcedId for the course. Omit to let
|
|
694
|
+
* Timeback allocate one. Fixed once the course exists.
|
|
695
|
+
*/
|
|
696
|
+
sourcedId?: string;
|
|
697
|
+
title: string;
|
|
698
|
+
subjects: string[];
|
|
699
|
+
grades: number[];
|
|
700
|
+
courseCode: string;
|
|
701
|
+
level: string;
|
|
702
|
+
gradingScheme: string;
|
|
703
|
+
metadata?: Record<string, unknown>;
|
|
704
|
+
};
|
|
705
|
+
component: {
|
|
706
|
+
title: string;
|
|
707
|
+
sortOrder: number;
|
|
708
|
+
prerequisites: string[];
|
|
709
|
+
prerequisiteCriteria: string;
|
|
710
|
+
};
|
|
711
|
+
resource: {
|
|
712
|
+
title: string;
|
|
713
|
+
vendorResourceId: string;
|
|
714
|
+
vendorId: string;
|
|
715
|
+
applicationId: string;
|
|
716
|
+
roles: string[];
|
|
717
|
+
importance: string;
|
|
718
|
+
metadata: {
|
|
719
|
+
type?: string;
|
|
720
|
+
launchUrl?: string;
|
|
721
|
+
toolProvider?: string;
|
|
722
|
+
instructionalMethod?: string;
|
|
723
|
+
subject?: string;
|
|
724
|
+
grades?: number[];
|
|
725
|
+
language?: string;
|
|
726
|
+
xp?: number;
|
|
727
|
+
[key: string]: unknown;
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
componentResource: {
|
|
731
|
+
title: string;
|
|
732
|
+
sortOrder: number;
|
|
733
|
+
lessonType: string | null;
|
|
734
|
+
};
|
|
735
|
+
};
|
|
736
|
+
verbose?: boolean;
|
|
823
737
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
738
|
+
interface PlatformTimebackSetupRequest {
|
|
739
|
+
gameId: string;
|
|
740
|
+
courses: DerivedPlatformCourseConfig[];
|
|
741
|
+
baseConfig: {
|
|
742
|
+
organization: {
|
|
743
|
+
name: string;
|
|
744
|
+
type: string;
|
|
745
|
+
identifier: string;
|
|
746
|
+
};
|
|
747
|
+
component: {
|
|
748
|
+
title: string;
|
|
749
|
+
titleSuffix?: string;
|
|
750
|
+
sortOrder: number;
|
|
751
|
+
prerequisites: string[];
|
|
752
|
+
prerequisiteCriteria: string;
|
|
753
|
+
};
|
|
754
|
+
resource: {
|
|
755
|
+
title: string;
|
|
756
|
+
titleSuffix?: string;
|
|
757
|
+
vendorResourceId: string;
|
|
758
|
+
vendorId: string;
|
|
759
|
+
applicationId: string;
|
|
760
|
+
roles: string[];
|
|
761
|
+
importance: string;
|
|
762
|
+
metadata: {
|
|
763
|
+
type?: string;
|
|
764
|
+
launchUrl?: string;
|
|
765
|
+
toolProvider?: string;
|
|
766
|
+
instructionalMethod?: string;
|
|
767
|
+
language?: string;
|
|
768
|
+
[key: string]: unknown;
|
|
769
|
+
};
|
|
770
|
+
};
|
|
771
|
+
componentResource: {
|
|
772
|
+
title: string;
|
|
773
|
+
titleSuffix?: string;
|
|
774
|
+
sortOrder: number;
|
|
775
|
+
lessonType: string | null;
|
|
776
|
+
};
|
|
777
|
+
};
|
|
778
|
+
verbose?: boolean;
|
|
828
779
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
780
|
+
interface PlatformTimebackSetupResponse {
|
|
781
|
+
integrations: GameTimebackIntegration[];
|
|
782
|
+
verbose?: {
|
|
783
|
+
integration: GameTimebackIntegration;
|
|
784
|
+
config: {
|
|
785
|
+
course: unknown;
|
|
786
|
+
component: unknown;
|
|
787
|
+
resource: unknown;
|
|
788
|
+
componentResource: unknown;
|
|
789
|
+
};
|
|
790
|
+
}[];
|
|
837
791
|
}
|
|
838
|
-
/** Why the answering game couldn't answer. */
|
|
839
|
-
type SkillStatusUnsupportedReason = 'not_implemented' | 'no_active_deployment' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
840
792
|
/**
|
|
841
|
-
*
|
|
842
|
-
*
|
|
793
|
+
* `base` courses form a game's grade ladder (one active per grade and subject).
|
|
794
|
+
* `hole_filling` courses are remediation courses Timeback enrolls students into
|
|
795
|
+
* after a failed test; each sits beside a base course.
|
|
843
796
|
*/
|
|
844
|
-
type
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
/**
|
|
863
|
-
* The TimeBack subject vocabulary. 'None' is canonical: TimeBack accepts it
|
|
864
|
-
* on courses and uses it as the fallback for unknown or missing subjects.
|
|
865
|
-
*/
|
|
866
|
-
type TimebackSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
867
|
-
/**
|
|
868
|
-
* Grade levels per AE OneRoster GradeEnum.
|
|
869
|
-
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
870
|
-
*/
|
|
871
|
-
type TimebackGrade = (typeof TIMEBACK_GRADES)[number];
|
|
872
|
-
/**
|
|
873
|
-
* Platform pedagogy levels for a single lesson of instruction.
|
|
874
|
-
* E1 teaches, E2/E3 practice and remediate, E4 is the mastery check.
|
|
875
|
-
*/
|
|
876
|
-
type ELevel = 'E1' | 'E2' | 'E3' | 'E4';
|
|
877
|
-
/**
|
|
878
|
-
* Valid Caliper subject values. The same vocabulary as OneRoster subjects.
|
|
879
|
-
*/
|
|
880
|
-
type CaliperSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
881
|
-
/**
|
|
882
|
-
* OneRoster organization types.
|
|
883
|
-
*/
|
|
884
|
-
type OrganizationType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
885
|
-
/**
|
|
886
|
-
* Lesson types for PowerPath integration.
|
|
887
|
-
*/
|
|
888
|
-
type LessonType = 'powerpath-100' | 'quiz' | 'test-out' | 'placement' | 'unit-test' | 'alpha-read-article' | null;
|
|
889
|
-
|
|
890
|
-
/**
|
|
891
|
-
* TimeBack Configuration Types
|
|
892
|
-
*
|
|
893
|
-
* Configuration interfaces for Organization, Course, Component,
|
|
894
|
-
* Resource, and complete TimeBack setup.
|
|
895
|
-
*
|
|
896
|
-
* @module types/timeback/config
|
|
897
|
-
*/
|
|
898
|
-
|
|
899
|
-
/**
|
|
900
|
-
* Organization configuration for TimeBack (user input - optionals allowed)
|
|
901
|
-
*/
|
|
902
|
-
interface OrganizationConfig {
|
|
903
|
-
/** Display name for your organization */
|
|
904
|
-
name?: string;
|
|
905
|
-
/** Organization type */
|
|
906
|
-
type?: OrganizationType;
|
|
907
|
-
/** Unique identifier (defaults to Playcademy's org) */
|
|
908
|
-
identifier?: string;
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Course goals for daily student targets
|
|
912
|
-
*/
|
|
913
|
-
interface CourseGoals {
|
|
914
|
-
/** Target XP students should earn per day */
|
|
915
|
-
dailyXp?: number;
|
|
916
|
-
/** Target lessons per day */
|
|
917
|
-
dailyLessons?: number;
|
|
918
|
-
/** Target active minutes per day */
|
|
919
|
-
dailyActiveMinutes?: number;
|
|
920
|
-
/** Target accuracy percentage */
|
|
921
|
-
dailyAccuracy?: number;
|
|
922
|
-
/** Target mastered units per day */
|
|
923
|
-
dailyMasteredUnits?: number;
|
|
797
|
+
type GameTimebackIntegrationKind = 'base' | 'hole_filling';
|
|
798
|
+
interface GameTimebackIntegration {
|
|
799
|
+
id: string;
|
|
800
|
+
gameId: string;
|
|
801
|
+
courseId: string;
|
|
802
|
+
grade: number;
|
|
803
|
+
subject: string;
|
|
804
|
+
/** Defaults to `base` when absent (responses from older servers). */
|
|
805
|
+
kind?: GameTimebackIntegrationKind;
|
|
806
|
+
/** The base course a hole-filling course belongs to; null for base courses. */
|
|
807
|
+
baseIntegrationId?: string | null;
|
|
808
|
+
status?: 'active' | 'deactivated';
|
|
809
|
+
totalXp?: number | null;
|
|
810
|
+
lastVerifiedAt?: Date | null;
|
|
811
|
+
deactivatedAt?: Date | null;
|
|
812
|
+
reactivatedAt?: Date | null;
|
|
813
|
+
createdAt: Date;
|
|
814
|
+
updatedAt: Date;
|
|
924
815
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
/** The type of course (e.g. 'optional', 'hole-filling', 'base') */
|
|
936
|
-
courseType?: 'base' | 'hole-filling' | 'optional' | 'Base' | 'Hole-Filling' | 'Optional';
|
|
937
|
-
/** Indicates whether the course is supplemental content */
|
|
938
|
-
isSupplemental?: boolean;
|
|
816
|
+
interface RemovedGameTimebackIntegration {
|
|
817
|
+
integration: GameTimebackIntegration;
|
|
818
|
+
title: string;
|
|
819
|
+
courseCode: string;
|
|
820
|
+
subject: TimebackSubject;
|
|
821
|
+
grade: number;
|
|
822
|
+
totalXp?: number | null;
|
|
823
|
+
masterableUnits?: number | null;
|
|
824
|
+
removedAt?: Date | string | null;
|
|
825
|
+
metadata?: CourseMetadata | null;
|
|
939
826
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
/** Boolean value to determine if a course is custom to an individual student */
|
|
949
|
-
isCustom?: boolean;
|
|
950
|
-
/** Signals whether a course is in production with students */
|
|
951
|
-
publishStatus?: 'draft' | 'testing' | 'published' | 'deactivated';
|
|
952
|
-
/** Whether this course appears in the TimeBack catalog for teachers and parents */
|
|
953
|
-
timebackVisible?: boolean;
|
|
954
|
-
/** Who to contact when issues reported with questions */
|
|
955
|
-
contactEmail?: string;
|
|
956
|
-
/** Primary app identifier */
|
|
957
|
-
primaryApp?: string;
|
|
958
|
-
/** Learning goals for students */
|
|
959
|
-
goals?: CourseGoals;
|
|
960
|
-
/** Course metrics and totals */
|
|
961
|
-
metrics?: CourseMetrics;
|
|
962
|
-
/** Vendor-specific metadata (e.g., AlphaLearn) */
|
|
963
|
-
[key: string]: unknown;
|
|
827
|
+
interface GameTimebackIntegrationConfig {
|
|
828
|
+
integration: GameTimebackIntegration;
|
|
829
|
+
title: string;
|
|
830
|
+
courseCode: string;
|
|
831
|
+
subject: TimebackSubject;
|
|
832
|
+
totalXp?: number | null;
|
|
833
|
+
masterableUnits?: number | null;
|
|
834
|
+
metadata?: CourseMetadata | null;
|
|
964
835
|
}
|
|
965
|
-
|
|
966
|
-
* Course configuration for TimeBack (user input)
|
|
967
|
-
*/
|
|
968
|
-
interface CourseConfig {
|
|
969
|
-
/** Allocated OneRoster sourcedId (set after creation) */
|
|
970
|
-
sourcedId?: string;
|
|
971
|
-
/** Course title (defaults to game name) */
|
|
836
|
+
interface UpdateGameTimebackIntegrationRequest {
|
|
972
837
|
title?: string;
|
|
973
|
-
/** Subjects (REQUIRED for TimeBack integration) */
|
|
974
|
-
subjects: TimebackSubject[];
|
|
975
|
-
/** Used when recording progress/sessions if not explicitly specified per event. */
|
|
976
|
-
defaultSubject?: TimebackSubject;
|
|
977
|
-
/** Grade levels (REQUIRED for TimeBack integration) */
|
|
978
|
-
grades: TimebackGrade[];
|
|
979
|
-
/** Short course code (optional, auto-generated) */
|
|
980
838
|
courseCode?: string;
|
|
981
|
-
|
|
982
|
-
level?: 'Elementary' | 'Middle' | 'High' | 'AP' | string;
|
|
983
|
-
/** Grading system */
|
|
984
|
-
gradingScheme?: 'STANDARD';
|
|
985
|
-
/** Total XP available in this course (REQUIRED before setup) */
|
|
839
|
+
subject?: TimebackSubject;
|
|
986
840
|
totalXp?: number | null;
|
|
987
|
-
/** Total masterable units in this course (REQUIRED before setup) */
|
|
988
841
|
masterableUnits?: number | null;
|
|
989
|
-
|
|
990
|
-
|
|
842
|
+
goals?: Partial<Record<keyof CourseGoals, number | null>> | null;
|
|
843
|
+
publishStatus?: string | null;
|
|
844
|
+
isSupplemental?: boolean;
|
|
845
|
+
timebackVisible?: boolean | null;
|
|
991
846
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
/** Required prior components */
|
|
1001
|
-
prerequisites?: string[];
|
|
1002
|
-
/** How prerequisites work */
|
|
1003
|
-
prerequisiteCriteria?: 'ALL' | 'ANY';
|
|
847
|
+
interface CreateGameTimebackIntegrationRequest {
|
|
848
|
+
title: string;
|
|
849
|
+
courseCode: string;
|
|
850
|
+
subject: TimebackSubject;
|
|
851
|
+
grade: TimebackGrade;
|
|
852
|
+
totalXp: number;
|
|
853
|
+
masterableUnits: number;
|
|
854
|
+
level?: string;
|
|
1004
855
|
}
|
|
1005
856
|
/**
|
|
1006
|
-
*
|
|
857
|
+
* Create a hole-filling course beside an existing base course. Subject and
|
|
858
|
+
* grade come from the base course; everything else defaults to a copy of it.
|
|
1007
859
|
*/
|
|
1008
|
-
interface
|
|
1009
|
-
/**
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
860
|
+
interface CreateGameTimebackHoleFillingCourseRequest {
|
|
861
|
+
/** Course ID of the active base course this hole-filling course belongs to. */
|
|
862
|
+
baseCourseId: string;
|
|
863
|
+
title: string;
|
|
864
|
+
courseCode: string;
|
|
865
|
+
totalXp: number;
|
|
866
|
+
masterableUnits: number;
|
|
867
|
+
/**
|
|
868
|
+
* Optional OneRoster sourcedId for the new course. Fixed once created; when
|
|
869
|
+
* omitted Timeback allocates one. This is the ID handed to Timeback.
|
|
870
|
+
*/
|
|
871
|
+
courseId?: string;
|
|
1016
872
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
/** Interactive resource metadata */
|
|
1034
|
-
metadata?: {
|
|
1035
|
-
/** Resource type */
|
|
1036
|
-
type?: 'interactive';
|
|
1037
|
-
/** Launch URL (defaults to Playcademy game URL) */
|
|
1038
|
-
launchUrl?: string;
|
|
1039
|
-
/** Platform name */
|
|
1040
|
-
toolProvider?: string;
|
|
1041
|
-
/** Teaching method */
|
|
1042
|
-
instructionalMethod?: 'exploratory' | 'direct-instruction';
|
|
1043
|
-
/** Subject area */
|
|
1044
|
-
subject?: TimebackSubject;
|
|
1045
|
-
/** Target grades */
|
|
1046
|
-
grades?: TimebackGrade[];
|
|
1047
|
-
/** Content language */
|
|
1048
|
-
language?: string;
|
|
1049
|
-
/** Base XP for completion */
|
|
1050
|
-
xp?: number;
|
|
1051
|
-
/** Playcademy-specific extensions */
|
|
1052
|
-
playcademy?: PlaycademyResourceMetadata;
|
|
873
|
+
interface TimebackVerificationResources {
|
|
874
|
+
course: {
|
|
875
|
+
found: boolean;
|
|
876
|
+
data?: unknown;
|
|
877
|
+
};
|
|
878
|
+
component: {
|
|
879
|
+
found: boolean;
|
|
880
|
+
data?: unknown;
|
|
881
|
+
};
|
|
882
|
+
resource: {
|
|
883
|
+
found: boolean;
|
|
884
|
+
data?: unknown;
|
|
885
|
+
};
|
|
886
|
+
componentResource: {
|
|
887
|
+
found: boolean;
|
|
888
|
+
data?: unknown;
|
|
1053
889
|
};
|
|
1054
890
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
title?: string;
|
|
1061
|
-
/** Display order */
|
|
1062
|
-
sortOrder?: number;
|
|
1063
|
-
/** Lesson type for PowerPath integration */
|
|
1064
|
-
lessonType?: LessonType;
|
|
1065
|
-
}
|
|
1066
|
-
interface TimebackCourseConfig {
|
|
1067
|
-
subject: string;
|
|
1068
|
-
grade: number;
|
|
1069
|
-
}
|
|
1070
|
-
interface DerivedPlatformCourseConfig extends TimebackCourseConfig {
|
|
1071
|
-
title: string;
|
|
1072
|
-
courseCode: string;
|
|
1073
|
-
level: string;
|
|
1074
|
-
metadata?: Record<string, unknown>;
|
|
1075
|
-
totalXp?: number | null;
|
|
1076
|
-
masterableUnits?: number | null;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
/**
|
|
1080
|
-
* TimeBack Client SDK DTOs
|
|
1081
|
-
*
|
|
1082
|
-
* Data transfer objects for the TimeBack client SDK including
|
|
1083
|
-
* progress tracking, session management, and activity completion.
|
|
1084
|
-
*
|
|
1085
|
-
* Note: TimebackClientConfig lives in @playcademy/timeback as it's
|
|
1086
|
-
* SDK configuration, not a DTO.
|
|
1087
|
-
*
|
|
1088
|
-
* @module types/timeback/client
|
|
1089
|
-
*/
|
|
1090
|
-
|
|
1091
|
-
/**
|
|
1092
|
-
* Known extensions for TimeBack Activity Metrics Collection
|
|
1093
|
-
*/
|
|
1094
|
-
interface TimebackActivityExtensions {
|
|
1095
|
-
/** Percentage complete (0-100) for the app course */
|
|
1096
|
-
pctCompleteApp?: number;
|
|
1097
|
-
/** Allow other arbitrary extensions */
|
|
1098
|
-
[key: string]: unknown;
|
|
891
|
+
interface TimebackVerifyCourseResult {
|
|
892
|
+
integration: GameTimebackIntegration;
|
|
893
|
+
resources: TimebackVerificationResources;
|
|
894
|
+
status: 'success' | 'error';
|
|
895
|
+
errors?: string[];
|
|
1099
896
|
}
|
|
1100
|
-
interface
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
currentMasteredUnits: number;
|
|
1104
|
-
attemptedMasteredUnits: number;
|
|
1105
|
-
appliedMasteredUnits: number;
|
|
1106
|
-
storedMasteredUnits: number;
|
|
1107
|
-
masterableUnits: number;
|
|
897
|
+
interface TimebackVerifyAllResponse {
|
|
898
|
+
status: 'success' | 'error';
|
|
899
|
+
results: TimebackVerifyCourseResult[];
|
|
1108
900
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
/**
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
901
|
+
interface HeartbeatRequestBase {
|
|
902
|
+
gameId: string;
|
|
903
|
+
studentId: string;
|
|
904
|
+
runId: string;
|
|
905
|
+
/**
|
|
906
|
+
* Per-session identifier.
|
|
907
|
+
*
|
|
908
|
+
* LEGACY SDK COMPATIBILITY (PR #711): optional because games deployed
|
|
909
|
+
* against the pre-resumable-sessions SDK do not send one. The
|
|
910
|
+
* platform falls back to `runId` when absent. Make this required
|
|
911
|
+
* once legacy SDK support is dropped.
|
|
912
|
+
*/
|
|
913
|
+
resumeId?: string;
|
|
914
|
+
activityData: {
|
|
915
|
+
activityId: string;
|
|
916
|
+
activityName?: string;
|
|
917
|
+
grade: TimebackGrade;
|
|
918
|
+
subject: TimebackSubject;
|
|
919
|
+
appName?: string;
|
|
920
|
+
sensorUrl?: string;
|
|
921
|
+
courseId?: string;
|
|
922
|
+
/** The student's enrollment in `courseId`, sent by the SDK with the course hint. */
|
|
923
|
+
enrollmentId?: string;
|
|
924
|
+
courseName?: string;
|
|
925
|
+
studentEmail?: string;
|
|
926
|
+
};
|
|
927
|
+
timingData: {
|
|
928
|
+
activeMs: number;
|
|
929
|
+
pausedMs: number;
|
|
930
|
+
};
|
|
931
|
+
isFinal?: boolean;
|
|
1131
932
|
}
|
|
1132
|
-
|
|
1133
|
-
* Score data for ending an activity
|
|
1134
|
-
*/
|
|
1135
|
-
interface EndActivityScoreData {
|
|
1136
|
-
/** Number of questions answered correctly */
|
|
1137
|
-
correctQuestions: number;
|
|
1138
|
-
/** Total number of questions */
|
|
1139
|
-
totalQuestions: number;
|
|
933
|
+
type HeartbeatWindowKey = {
|
|
1140
934
|
/**
|
|
1141
|
-
*
|
|
935
|
+
* Absolute start-of-window timestamp (epoch ms). Preferred dedupe key
|
|
936
|
+
* for the post-#711 SDK.
|
|
1142
937
|
*
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1146
|
-
* decides the award, and a value provided here travels only as the
|
|
1147
|
-
* `childXpSuggested` audit suggestion.
|
|
938
|
+
* LEGACY SDK COMPATIBILITY (PR #711): callers must provide exactly
|
|
939
|
+
* one of this field or `windowSequence`. Make this the only allowed
|
|
940
|
+
* field once legacy SDK support is dropped.
|
|
1148
941
|
*/
|
|
1149
|
-
|
|
942
|
+
windowStartedAtMs: number;
|
|
943
|
+
windowSequence?: never;
|
|
944
|
+
} | {
|
|
945
|
+
windowStartedAtMs?: never;
|
|
1150
946
|
/**
|
|
1151
|
-
*
|
|
947
|
+
* LEGACY SDK COMPATIBILITY (PR #711).
|
|
1152
948
|
*
|
|
1153
|
-
*
|
|
1154
|
-
*
|
|
1155
|
-
* the
|
|
1156
|
-
* returns warning metadata.
|
|
949
|
+
* Monotonic window counter emitted by the pre-resumable-sessions
|
|
950
|
+
* SDK. Remove this field once every deployed game has redeployed
|
|
951
|
+
* against the post-#711 SDK + edge-play runtime.
|
|
1157
952
|
*/
|
|
1158
|
-
|
|
953
|
+
windowSequence: number;
|
|
954
|
+
};
|
|
955
|
+
type HeartbeatRequest = HeartbeatRequestBase & HeartbeatWindowKey;
|
|
956
|
+
interface EndActivityRequest {
|
|
957
|
+
gameId: string;
|
|
958
|
+
studentId: string;
|
|
959
|
+
runId?: string;
|
|
1159
960
|
/**
|
|
1160
|
-
*
|
|
961
|
+
* Per-session identifier.
|
|
1161
962
|
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
* platform
|
|
1165
|
-
*
|
|
963
|
+
* LEGACY SDK COMPATIBILITY (PR #711): optional because games deployed
|
|
964
|
+
* against the pre-resumable-sessions SDK do not send one. The
|
|
965
|
+
* platform falls back to `runId`, or mints a server-side UUID, when
|
|
966
|
+
* absent. Make this required once legacy SDK support is dropped.
|
|
1166
967
|
*/
|
|
968
|
+
resumeId?: string;
|
|
969
|
+
activityData: {
|
|
970
|
+
activityId: string;
|
|
971
|
+
activityName?: string;
|
|
972
|
+
grade: TimebackGrade;
|
|
973
|
+
subject: TimebackSubject;
|
|
974
|
+
appName?: string;
|
|
975
|
+
sensorUrl?: string;
|
|
976
|
+
courseId?: string;
|
|
977
|
+
/** The student's enrollment in `courseId`, sent by the SDK with the course hint. */
|
|
978
|
+
enrollmentId?: string;
|
|
979
|
+
courseName?: string;
|
|
980
|
+
studentEmail?: string;
|
|
981
|
+
};
|
|
982
|
+
scoreData: {
|
|
983
|
+
correctQuestions: number;
|
|
984
|
+
totalQuestions: number;
|
|
985
|
+
};
|
|
986
|
+
timingData: {
|
|
987
|
+
durationSeconds: number;
|
|
988
|
+
};
|
|
989
|
+
sessionTimingData?: {
|
|
990
|
+
activeSeconds: number;
|
|
991
|
+
inactiveSeconds?: number;
|
|
992
|
+
};
|
|
993
|
+
xpEarned: number;
|
|
994
|
+
masteredUnits?: number;
|
|
1167
995
|
masteredUnitsAbsolute?: number;
|
|
1168
|
-
/** Optional arbitrary extensions to include in the Caliper event */
|
|
1169
996
|
extensions?: TimebackActivityExtensions;
|
|
1170
997
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
998
|
+
interface EndActivityResponse {
|
|
999
|
+
status: 'ok';
|
|
1000
|
+
/** The course the activity was recorded against. */
|
|
1001
|
+
courseId: string;
|
|
1002
|
+
/**
|
|
1003
|
+
* `'hole_filling'` when the activity was recorded against a hole-filling
|
|
1004
|
+
* course (the student is enrolled there, or the game passed its
|
|
1005
|
+
* `courseId`); `'base'` otherwise.
|
|
1006
|
+
*/
|
|
1007
|
+
courseKind?: GameTimebackIntegrationKind;
|
|
1008
|
+
xpAwarded: number;
|
|
1009
|
+
masteredUnits?: number;
|
|
1010
|
+
pctCompleteApp?: number;
|
|
1011
|
+
warnings?: MasteryWriteWarning[];
|
|
1012
|
+
/**
|
|
1013
|
+
* Set when this submission was stopped because a completion for the same
|
|
1014
|
+
* run (`runId`) and activity had already been recorded — the platform's
|
|
1015
|
+
* idempotency guard blocked a duplicate emit (PLA-142). This call awarded
|
|
1016
|
+
* nothing new, but the award may well have happened: if the original
|
|
1017
|
+
* response was lost in transit (e.g. a gateway timeout followed by an SDK
|
|
1018
|
+
* retry), this is the only response the caller ever sees for a run that
|
|
1019
|
+
* fully succeeded. The response therefore echoes what the run earned —
|
|
1020
|
+
* `xpAwarded`, `masteredUnits`, and `pctCompleteApp` are the values
|
|
1021
|
+
* recorded when the completion was confirmed (zeros, with no
|
|
1022
|
+
* `pctCompleteApp`, only for runs recorded before award echoing existed).
|
|
1023
|
+
* Treat it as "already recorded — here's what it earned", and do not
|
|
1024
|
+
* count the run again.
|
|
1025
|
+
*/
|
|
1026
|
+
blockedByDedupeGuard?: boolean;
|
|
1185
1027
|
}
|
|
1186
|
-
interface
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1028
|
+
interface StudentHighestGradeMasteredResponse {
|
|
1029
|
+
/** Subject used for the highest-grade-mastered lookup */
|
|
1030
|
+
subject: TimebackSubject;
|
|
1031
|
+
/** EduBridge highestGradeOverall normalized to Playcademy's numeric grade type */
|
|
1032
|
+
highestGradeMastered: TimebackGrade | null;
|
|
1033
|
+
/** Normalized source grades returned by EduBridge for the rollup. */
|
|
1034
|
+
grades: {
|
|
1035
|
+
ritGrade: TimebackGrade | null;
|
|
1036
|
+
edulasticGrade: TimebackGrade | null;
|
|
1037
|
+
placementGrade: TimebackGrade | null;
|
|
1038
|
+
testOutGrade: TimebackGrade | null;
|
|
1039
|
+
highestGradeOverall: TimebackGrade | null;
|
|
1194
1040
|
};
|
|
1195
|
-
activity?: {
|
|
1196
|
-
id?: string;
|
|
1197
|
-
name?: string;
|
|
1198
|
-
extensions?: Record<string, unknown> | null;
|
|
1199
|
-
};
|
|
1200
|
-
course?: {
|
|
1201
|
-
id?: string;
|
|
1202
|
-
name?: string;
|
|
1203
|
-
};
|
|
1204
|
-
items?: TimebackStoredCaliperMetric[];
|
|
1205
|
-
extensions?: Record<string, unknown> | null;
|
|
1206
|
-
[key: string]: unknown;
|
|
1207
|
-
}
|
|
1208
|
-
interface TimebackStoredCaliperEvent {
|
|
1209
|
-
id: number;
|
|
1210
|
-
externalId: string;
|
|
1211
|
-
sensor: string;
|
|
1212
|
-
type: string;
|
|
1213
|
-
profile?: string;
|
|
1214
|
-
action: string;
|
|
1215
|
-
eventTime: string;
|
|
1216
|
-
sendTime: string;
|
|
1217
|
-
updated_at: string | null;
|
|
1218
|
-
created_at: string;
|
|
1219
|
-
deleted_at: string | null;
|
|
1220
|
-
actor: TimebackStoredCaliperEntity;
|
|
1221
|
-
object: TimebackStoredCaliperEntity;
|
|
1222
|
-
generated?: TimebackStoredCaliperEntity | null;
|
|
1223
|
-
target?: TimebackStoredCaliperEntity | null;
|
|
1224
|
-
referrer?: TimebackStoredCaliperEntity | null;
|
|
1225
|
-
edApp?: TimebackStoredCaliperEntity | null;
|
|
1226
|
-
group?: TimebackStoredCaliperEntity | null;
|
|
1227
|
-
membership?: TimebackStoredCaliperEntity | null;
|
|
1228
|
-
session?: TimebackStoredCaliperEntity | null;
|
|
1229
|
-
federatedSession?: TimebackStoredCaliperEntity | null;
|
|
1230
|
-
extensions?: Record<string, unknown> | null;
|
|
1231
|
-
clientAppId?: string | null;
|
|
1232
1041
|
}
|
|
1233
|
-
|
|
1234
1042
|
/**
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
*
|
|
1238
|
-
* `/__playcademy/metrics` route.
|
|
1043
|
+
* Highest-ranking OneRoster account role for a user shown in the admin
|
|
1044
|
+
* dashboard. Precedence: administrator > teacher > student. Accounts whose
|
|
1045
|
+
* roles fall outside those three (aide, guardian, …) resolve to `null`.
|
|
1239
1046
|
*/
|
|
1240
|
-
|
|
1241
|
-
|
|
1047
|
+
type TimebackAccountRole = 'administrator' | 'teacher' | 'student';
|
|
1048
|
+
interface TimebackRosterStudent {
|
|
1242
1049
|
studentId: string;
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
/** Total active time in seconds the game has recorded for this course. */
|
|
1258
|
-
activeTimeSeconds?: number;
|
|
1259
|
-
/** Optional per-run breakdown keyed by Timeback run ID. */
|
|
1260
|
-
activities?: GameRunMetrics[];
|
|
1261
|
-
}
|
|
1262
|
-
interface GameRunMetrics {
|
|
1263
|
-
runId: string;
|
|
1264
|
-
activityId: string;
|
|
1265
|
-
activityName?: string;
|
|
1266
|
-
totalXp?: number;
|
|
1267
|
-
masteredUnits?: number;
|
|
1268
|
-
activeTimeSeconds?: number;
|
|
1269
|
-
score?: number;
|
|
1270
|
-
}
|
|
1271
|
-
type GameMetricsUnsupportedReason = 'no_timeback_integration' | 'no_active_deployment' | 'route_not_implemented' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
1272
|
-
type GameMetricsProxyResponse = {
|
|
1273
|
-
supported: true;
|
|
1274
|
-
metrics: GameMetricsResponse;
|
|
1275
|
-
} | {
|
|
1276
|
-
supported: false;
|
|
1277
|
-
reason: GameMetricsUnsupportedReason;
|
|
1278
|
-
details?: string;
|
|
1279
|
-
};
|
|
1280
|
-
type GameMetricComparisonMetric = 'xp' | 'mastery' | 'time' | 'score';
|
|
1281
|
-
type GameMetricComparisonKind = 'number' | 'time' | 'percent';
|
|
1282
|
-
type GameMetricComparisonRowStatus = 'matched' | 'discrepant' | 'not_reported_by_game' | 'not_recorded_by_timeback';
|
|
1283
|
-
interface GameMetricComparisonRow {
|
|
1284
|
-
metric: GameMetricComparisonMetric;
|
|
1285
|
-
kind: GameMetricComparisonKind;
|
|
1286
|
-
status: GameMetricComparisonRowStatus;
|
|
1287
|
-
timebackValue?: number;
|
|
1288
|
-
gameValue?: number;
|
|
1289
|
-
delta?: number;
|
|
1050
|
+
enrollmentId: string | null;
|
|
1051
|
+
classId: string | null;
|
|
1052
|
+
className: string | null;
|
|
1053
|
+
name: string;
|
|
1054
|
+
email: string | null;
|
|
1055
|
+
primaryRole: TimebackAccountRole | null;
|
|
1056
|
+
analyticsUnavailable: boolean;
|
|
1057
|
+
totalXp: number;
|
|
1058
|
+
todayXp: number;
|
|
1059
|
+
activeTimeSeconds: number;
|
|
1060
|
+
masteredUnits: number;
|
|
1061
|
+
masterableUnits?: number;
|
|
1062
|
+
pctCompleteApp?: number;
|
|
1063
|
+
inactive?: boolean;
|
|
1290
1064
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
discrepancyCount: number;
|
|
1296
|
-
reason?: GameMetricsUnsupportedReason;
|
|
1065
|
+
interface TimebackRosterResponse {
|
|
1066
|
+
gameId: string;
|
|
1067
|
+
courseId: string;
|
|
1068
|
+
students: TimebackRosterStudent[];
|
|
1297
1069
|
}
|
|
1298
|
-
interface
|
|
1299
|
-
|
|
1070
|
+
interface TimebackStudentHistoryPoint {
|
|
1071
|
+
date: string;
|
|
1072
|
+
xpEarned: number;
|
|
1073
|
+
activeTimeSeconds: number;
|
|
1074
|
+
masteredUnits: number;
|
|
1300
1075
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
sequence?: number;
|
|
1076
|
+
type CourseCompletionStatus = 'none' | 'complete' | 'incomplete';
|
|
1077
|
+
interface TimebackStudentEnrollmentSummary {
|
|
1078
|
+
enrollmentId: string;
|
|
1079
|
+
status: 'active' | 'tobedeleted';
|
|
1080
|
+
beginDate: string | null;
|
|
1081
|
+
endDate: string | null;
|
|
1082
|
+
analyticsUnavailable: boolean;
|
|
1083
|
+
totalXp: number;
|
|
1084
|
+
todayXp: number;
|
|
1085
|
+
activeTimeSeconds: number;
|
|
1086
|
+
masteredUnits: number;
|
|
1087
|
+
pctCompleteApp?: number;
|
|
1088
|
+
history: TimebackStudentHistoryPoint[];
|
|
1315
1089
|
}
|
|
1316
|
-
interface
|
|
1317
|
-
|
|
1090
|
+
interface TimebackStudentCourseOverview {
|
|
1091
|
+
courseId: string;
|
|
1318
1092
|
title: string;
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1093
|
+
grade: number;
|
|
1094
|
+
subject: string;
|
|
1095
|
+
enrollmentId: string | null;
|
|
1096
|
+
analyticsUnavailable: boolean;
|
|
1097
|
+
totalXp: number;
|
|
1098
|
+
todayXp: number;
|
|
1099
|
+
activeTimeSeconds: number;
|
|
1100
|
+
masteredUnits: number;
|
|
1101
|
+
masterableUnits?: number;
|
|
1102
|
+
pctCompleteApp?: number;
|
|
1103
|
+
completionStatus: CourseCompletionStatus;
|
|
1104
|
+
history: TimebackStudentHistoryPoint[];
|
|
1105
|
+
inactive?: boolean;
|
|
1106
|
+
/** All enrollment records for this course, sorted active-first then by most recent. */
|
|
1107
|
+
enrollments?: TimebackStudentEnrollmentSummary[];
|
|
1324
1108
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1109
|
+
type TimebackRecentActivityKind = 'activity' | 'activity-in-progress' | 'time-spent' | 'remediation-xp' | 'remediation-time' | 'remediation-mastery' | 'course-completed' | 'course-resumed';
|
|
1110
|
+
interface TimebackRecentActivity {
|
|
1111
|
+
id: string;
|
|
1112
|
+
kind: TimebackRecentActivityKind;
|
|
1113
|
+
occurredAt: string;
|
|
1114
|
+
courseId: string;
|
|
1115
|
+
title: string;
|
|
1116
|
+
activityId?: string;
|
|
1117
|
+
appName?: string;
|
|
1118
|
+
reason?: string;
|
|
1119
|
+
score?: number;
|
|
1120
|
+
xpDelta?: number;
|
|
1121
|
+
timeDeltaSeconds?: number;
|
|
1122
|
+
masteredUnitsDelta?: number;
|
|
1123
|
+
runId?: string;
|
|
1124
|
+
sessionCount?: number;
|
|
1125
|
+
gameMetricsComparison?: GameRunMetricsComparisonSummary;
|
|
1126
|
+
gameMetricsVerification?: TimebackMetricDiscrepancyVerification;
|
|
1330
1127
|
}
|
|
1331
|
-
interface
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
normalMinimum?: number;
|
|
1337
|
-
defaultValue?: {
|
|
1338
|
-
value?: unknown;
|
|
1128
|
+
interface TimebackStudentOverviewResponse {
|
|
1129
|
+
student: {
|
|
1130
|
+
studentId: string;
|
|
1131
|
+
name: string;
|
|
1132
|
+
email: string | null;
|
|
1339
1133
|
};
|
|
1134
|
+
courses: TimebackStudentCourseOverview[];
|
|
1340
1135
|
}
|
|
1341
|
-
interface
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
qtiVersion?: string;
|
|
1345
|
-
timeLimit?: number;
|
|
1346
|
-
maxAttempts?: number;
|
|
1347
|
-
toolsEnabled?: Record<string, boolean>;
|
|
1348
|
-
'qti-test-part': QtiTestPart[];
|
|
1349
|
-
'qti-outcome-declaration'?: QtiOutcomeDeclaration[];
|
|
1350
|
-
metadata?: Record<string, unknown>;
|
|
1351
|
-
rawXml?: string;
|
|
1352
|
-
content?: Record<string, unknown>;
|
|
1353
|
-
createdAt?: string;
|
|
1354
|
-
updatedAt?: string;
|
|
1136
|
+
interface TimebackStudentActivityResponse {
|
|
1137
|
+
activities: TimebackRecentActivity[];
|
|
1138
|
+
hasMore: boolean;
|
|
1355
1139
|
}
|
|
1356
|
-
interface
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
pages: number;
|
|
1361
|
-
limit: number;
|
|
1362
|
-
sort: string;
|
|
1363
|
-
order: 'asc' | 'desc';
|
|
1140
|
+
interface TimebackActivityDetailResponse {
|
|
1141
|
+
activity: TimebackRecentActivity;
|
|
1142
|
+
rawEvents: TimebackStoredCaliperEvent[];
|
|
1143
|
+
gameMetricsComparison?: GameRunMetricsComparison;
|
|
1364
1144
|
}
|
|
1365
|
-
interface
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
correctResponse?: {
|
|
1370
|
-
value: string[];
|
|
1371
|
-
};
|
|
1145
|
+
interface TimebackGradeLevelTestStudent {
|
|
1146
|
+
studentId: string;
|
|
1147
|
+
name: string;
|
|
1148
|
+
email: string | null;
|
|
1372
1149
|
}
|
|
1373
|
-
interface
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
preInteraction?: string;
|
|
1381
|
-
interaction?: Record<string, unknown>;
|
|
1382
|
-
postInteraction?: string;
|
|
1383
|
-
responseDeclarations?: QtiResponseDeclaration[];
|
|
1384
|
-
outcomeDeclarations?: QtiOutcomeDeclaration[];
|
|
1385
|
-
responseProcessing?: Record<string, unknown>;
|
|
1386
|
-
metadata?: Record<string, unknown>;
|
|
1387
|
-
modalFeedback?: unknown[];
|
|
1388
|
-
feedbackInline?: unknown[];
|
|
1389
|
-
feedbackBlock?: unknown[];
|
|
1390
|
-
rubrics?: unknown[];
|
|
1391
|
-
stimulus?: unknown;
|
|
1392
|
-
rawXml?: string;
|
|
1393
|
-
content?: Record<string, unknown>;
|
|
1394
|
-
createdAt?: string;
|
|
1395
|
-
updatedAt?: string;
|
|
1396
|
-
}
|
|
1397
|
-
interface QtiAssessmentItemListResponse {
|
|
1398
|
-
items: QtiAssessmentItem[];
|
|
1399
|
-
total: number;
|
|
1400
|
-
page: number;
|
|
1401
|
-
pages: number;
|
|
1402
|
-
limit: number;
|
|
1403
|
-
sort: string;
|
|
1404
|
-
order: 'asc' | 'desc';
|
|
1150
|
+
interface TimebackGradeLevelTestStandardRef {
|
|
1151
|
+
source: string;
|
|
1152
|
+
id: string;
|
|
1153
|
+
identifier?: string;
|
|
1154
|
+
name?: string;
|
|
1155
|
+
description?: string;
|
|
1156
|
+
domain?: string;
|
|
1405
1157
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
sourceItemIdentifier: string;
|
|
1158
|
+
interface TimebackGradeLevelTestStandardSummary extends TimebackGradeLevelTestStandardRef {
|
|
1159
|
+
attempted: number;
|
|
1160
|
+
missed: number;
|
|
1410
1161
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1162
|
+
interface TimebackGradeLevelTestSummary {
|
|
1163
|
+
resultId: string;
|
|
1164
|
+
lineItemId: string;
|
|
1165
|
+
studentId: string;
|
|
1166
|
+
testName: string;
|
|
1167
|
+
testType: string;
|
|
1168
|
+
provider: string;
|
|
1169
|
+
subject: string;
|
|
1170
|
+
grade: number | null;
|
|
1171
|
+
score: number | null;
|
|
1172
|
+
scorePercentile: number | null;
|
|
1173
|
+
scoreStatus: string;
|
|
1174
|
+
scoreDate: string | null;
|
|
1175
|
+
assignmentId?: string;
|
|
1176
|
+
qtiTestId?: string;
|
|
1177
|
+
questionCount?: number;
|
|
1178
|
+
correctQuestionCount?: number;
|
|
1421
1179
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1180
|
+
type TimebackGradeLevelQuestionCorrectness = 'correct' | 'incorrect' | 'partial' | 'unknown';
|
|
1181
|
+
interface TimebackGradeLevelTestQuestionResult {
|
|
1182
|
+
lineItemId: string;
|
|
1183
|
+
resultId: string | null;
|
|
1424
1184
|
title: string;
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
* setup, verification, and activity completion.
|
|
1434
|
-
*
|
|
1435
|
-
* @module types/timeback/api
|
|
1436
|
-
*/
|
|
1437
|
-
|
|
1438
|
-
interface PopulateStudentResponse {
|
|
1439
|
-
status: string;
|
|
1440
|
-
message?: string;
|
|
1185
|
+
description?: string;
|
|
1186
|
+
correctness: TimebackGradeLevelQuestionCorrectness;
|
|
1187
|
+
score: number | null;
|
|
1188
|
+
scorePercentile: number | null;
|
|
1189
|
+
scoreStatus: string | null;
|
|
1190
|
+
standards: TimebackGradeLevelTestStandardRef[];
|
|
1191
|
+
qtiQuestion?: QtiTestQuestionRef | null;
|
|
1192
|
+
feedback?: string | null;
|
|
1441
1193
|
}
|
|
1442
|
-
interface
|
|
1194
|
+
interface TimebackGradeLevelTestResultsResponse {
|
|
1443
1195
|
gameId: string;
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
course: {
|
|
1451
|
-
title: string;
|
|
1452
|
-
subjects: string[];
|
|
1453
|
-
grades: number[];
|
|
1454
|
-
courseCode: string;
|
|
1455
|
-
level: string;
|
|
1456
|
-
gradingScheme: string;
|
|
1457
|
-
metadata?: Record<string, unknown>;
|
|
1458
|
-
};
|
|
1459
|
-
component: {
|
|
1460
|
-
title: string;
|
|
1461
|
-
sortOrder: number;
|
|
1462
|
-
prerequisites: string[];
|
|
1463
|
-
prerequisiteCriteria: string;
|
|
1464
|
-
};
|
|
1465
|
-
resource: {
|
|
1466
|
-
title: string;
|
|
1467
|
-
vendorResourceId: string;
|
|
1468
|
-
vendorId: string;
|
|
1469
|
-
applicationId: string;
|
|
1470
|
-
roles: string[];
|
|
1471
|
-
importance: string;
|
|
1472
|
-
metadata: {
|
|
1473
|
-
type?: string;
|
|
1474
|
-
launchUrl?: string;
|
|
1475
|
-
toolProvider?: string;
|
|
1476
|
-
instructionalMethod?: string;
|
|
1477
|
-
subject?: string;
|
|
1478
|
-
grades?: number[];
|
|
1479
|
-
language?: string;
|
|
1480
|
-
xp?: number;
|
|
1481
|
-
[key: string]: unknown;
|
|
1482
|
-
};
|
|
1483
|
-
};
|
|
1484
|
-
componentResource: {
|
|
1485
|
-
title: string;
|
|
1486
|
-
sortOrder: number;
|
|
1487
|
-
lessonType: string | null;
|
|
1488
|
-
};
|
|
1489
|
-
};
|
|
1490
|
-
verbose?: boolean;
|
|
1196
|
+
courseId: string;
|
|
1197
|
+
subject: TimebackSubject;
|
|
1198
|
+
grade: TimebackGrade;
|
|
1199
|
+
student: TimebackGradeLevelTestStudent;
|
|
1200
|
+
highestGradeMastered: StudentHighestGradeMasteredResponse | null;
|
|
1201
|
+
results: TimebackGradeLevelTestSummary[];
|
|
1491
1202
|
}
|
|
1492
|
-
interface
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
identifier: string;
|
|
1500
|
-
};
|
|
1501
|
-
component: {
|
|
1502
|
-
title: string;
|
|
1503
|
-
titleSuffix?: string;
|
|
1504
|
-
sortOrder: number;
|
|
1505
|
-
prerequisites: string[];
|
|
1506
|
-
prerequisiteCriteria: string;
|
|
1507
|
-
};
|
|
1508
|
-
resource: {
|
|
1509
|
-
title: string;
|
|
1510
|
-
titleSuffix?: string;
|
|
1511
|
-
vendorResourceId: string;
|
|
1512
|
-
vendorId: string;
|
|
1513
|
-
applicationId: string;
|
|
1514
|
-
roles: string[];
|
|
1515
|
-
importance: string;
|
|
1516
|
-
metadata: {
|
|
1517
|
-
type?: string;
|
|
1518
|
-
launchUrl?: string;
|
|
1519
|
-
toolProvider?: string;
|
|
1520
|
-
instructionalMethod?: string;
|
|
1521
|
-
language?: string;
|
|
1522
|
-
[key: string]: unknown;
|
|
1523
|
-
};
|
|
1524
|
-
};
|
|
1525
|
-
componentResource: {
|
|
1526
|
-
title: string;
|
|
1527
|
-
titleSuffix?: string;
|
|
1528
|
-
sortOrder: number;
|
|
1529
|
-
lessonType: string | null;
|
|
1530
|
-
};
|
|
1531
|
-
};
|
|
1532
|
-
verbose?: boolean;
|
|
1203
|
+
interface TimebackGradeLevelTestReviewResponse {
|
|
1204
|
+
parentResultId: string;
|
|
1205
|
+
parentLineItemId: string;
|
|
1206
|
+
studentId: string;
|
|
1207
|
+
qtiQuestionCount: number;
|
|
1208
|
+
questions: TimebackGradeLevelTestQuestionResult[];
|
|
1209
|
+
standards: TimebackGradeLevelTestStandardSummary[];
|
|
1533
1210
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
config: {
|
|
1539
|
-
course: unknown;
|
|
1540
|
-
component: unknown;
|
|
1541
|
-
resource: unknown;
|
|
1542
|
-
componentResource: unknown;
|
|
1543
|
-
};
|
|
1544
|
-
}[];
|
|
1211
|
+
type TimebackDiscrepancyQueueWindow = 'all' | 'today' | 'yesterday' | 'this-week' | 'last-week' | 'custom';
|
|
1212
|
+
interface TimebackDiscrepancyQueueDateRange {
|
|
1213
|
+
startDate?: string;
|
|
1214
|
+
endDate?: string;
|
|
1545
1215
|
}
|
|
1546
|
-
interface
|
|
1547
|
-
id: string;
|
|
1216
|
+
interface TimebackMetricDiscrepancyVerification {
|
|
1548
1217
|
gameId: string;
|
|
1549
1218
|
courseId: string;
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
deactivatedAt?: Date | null;
|
|
1556
|
-
reactivatedAt?: Date | null;
|
|
1557
|
-
createdAt: Date;
|
|
1558
|
-
updatedAt: Date;
|
|
1219
|
+
studentId: string;
|
|
1220
|
+
runId: string;
|
|
1221
|
+
activityId?: string | null;
|
|
1222
|
+
verifiedAt: string;
|
|
1223
|
+
verifiedByUserId?: string | null;
|
|
1559
1224
|
}
|
|
1560
|
-
interface
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
subject: TimebackSubject;
|
|
1565
|
-
grade: number;
|
|
1566
|
-
totalXp?: number | null;
|
|
1567
|
-
masterableUnits?: number | null;
|
|
1568
|
-
removedAt?: Date | string | null;
|
|
1569
|
-
metadata?: CourseMetadata | null;
|
|
1225
|
+
interface TimebackDiscrepancyQueueStudent {
|
|
1226
|
+
studentId: string;
|
|
1227
|
+
name: string;
|
|
1228
|
+
email: string | null;
|
|
1570
1229
|
}
|
|
1571
|
-
interface
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
masterableUnits?: number | null;
|
|
1578
|
-
metadata?: CourseMetadata | null;
|
|
1579
|
-
}
|
|
1580
|
-
interface UpdateGameTimebackIntegrationRequest {
|
|
1581
|
-
title?: string;
|
|
1582
|
-
courseCode?: string;
|
|
1583
|
-
subject?: TimebackSubject;
|
|
1584
|
-
totalXp?: number | null;
|
|
1585
|
-
masterableUnits?: number | null;
|
|
1586
|
-
goals?: Partial<Record<keyof CourseGoals, number | null>> | null;
|
|
1587
|
-
publishStatus?: string | null;
|
|
1588
|
-
isSupplemental?: boolean;
|
|
1589
|
-
timebackVisible?: boolean | null;
|
|
1230
|
+
interface TimebackDiscrepancyQueueEventPage {
|
|
1231
|
+
offset: number;
|
|
1232
|
+
limit: number;
|
|
1233
|
+
total: number;
|
|
1234
|
+
hasPrevious: boolean;
|
|
1235
|
+
hasNext: boolean;
|
|
1590
1236
|
}
|
|
1591
|
-
interface
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
masterableUnits: number;
|
|
1598
|
-
level?: string;
|
|
1237
|
+
interface TimebackMetricDiscrepancyQueueItem {
|
|
1238
|
+
student: TimebackDiscrepancyQueueStudent;
|
|
1239
|
+
activity: TimebackRecentActivity;
|
|
1240
|
+
gameMetricsComparison: GameRunMetricsComparison;
|
|
1241
|
+
verification?: TimebackMetricDiscrepancyVerification;
|
|
1242
|
+
comparisonSource?: 'hydrated' | 'preliminary';
|
|
1599
1243
|
}
|
|
1600
|
-
interface
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
data?: unknown;
|
|
1612
|
-
};
|
|
1613
|
-
componentResource: {
|
|
1614
|
-
found: boolean;
|
|
1615
|
-
data?: unknown;
|
|
1616
|
-
};
|
|
1244
|
+
interface TimebackMetricDiscrepancyQueueResponse {
|
|
1245
|
+
gameId: string;
|
|
1246
|
+
courseId: string;
|
|
1247
|
+
window: TimebackDiscrepancyQueueWindow;
|
|
1248
|
+
dateRange: TimebackDiscrepancyQueueDateRange;
|
|
1249
|
+
studentId?: string;
|
|
1250
|
+
students: TimebackDiscrepancyQueueStudent[];
|
|
1251
|
+
discrepancyMetricScopes: GameMetricComparisonMetric[];
|
|
1252
|
+
eventPage: TimebackDiscrepancyQueueEventPage;
|
|
1253
|
+
includeVerified: boolean;
|
|
1254
|
+
items: TimebackMetricDiscrepancyQueueItem[];
|
|
1617
1255
|
}
|
|
1618
|
-
interface
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
errors?: string[];
|
|
1256
|
+
interface VerifyTimebackMetricDiscrepancyRequest {
|
|
1257
|
+
studentId: string;
|
|
1258
|
+
runId: string;
|
|
1259
|
+
activityId?: string;
|
|
1623
1260
|
}
|
|
1624
|
-
interface
|
|
1625
|
-
status: '
|
|
1626
|
-
|
|
1261
|
+
interface VerifyTimebackMetricDiscrepancyResponse {
|
|
1262
|
+
status: 'ok';
|
|
1263
|
+
verification: TimebackMetricDiscrepancyVerification;
|
|
1627
1264
|
}
|
|
1628
|
-
interface
|
|
1265
|
+
interface GrantTimebackXpRequest {
|
|
1629
1266
|
gameId: string;
|
|
1267
|
+
courseId: string;
|
|
1630
1268
|
studentId: string;
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
* LEGACY SDK COMPATIBILITY (PR #711): optional because games deployed
|
|
1636
|
-
* against the pre-resumable-sessions SDK do not send one. The
|
|
1637
|
-
* platform falls back to `runId` when absent. Make this required
|
|
1638
|
-
* once legacy SDK support is dropped.
|
|
1639
|
-
*/
|
|
1640
|
-
resumeId?: string;
|
|
1641
|
-
activityData: {
|
|
1642
|
-
activityId: string;
|
|
1643
|
-
activityName?: string;
|
|
1644
|
-
grade: TimebackGrade;
|
|
1645
|
-
subject: TimebackSubject;
|
|
1646
|
-
appName?: string;
|
|
1647
|
-
sensorUrl?: string;
|
|
1648
|
-
courseId?: string;
|
|
1649
|
-
courseName?: string;
|
|
1650
|
-
studentEmail?: string;
|
|
1651
|
-
};
|
|
1652
|
-
timingData: {
|
|
1653
|
-
activeMs: number;
|
|
1654
|
-
pausedMs: number;
|
|
1655
|
-
};
|
|
1656
|
-
isFinal?: boolean;
|
|
1269
|
+
xp: number;
|
|
1270
|
+
reason: string;
|
|
1271
|
+
date?: string;
|
|
1272
|
+
useCurrentTime?: boolean;
|
|
1657
1273
|
}
|
|
1658
|
-
|
|
1659
|
-
/**
|
|
1660
|
-
* Absolute start-of-window timestamp (epoch ms). Preferred dedupe key
|
|
1661
|
-
* for the post-#711 SDK.
|
|
1662
|
-
*
|
|
1663
|
-
* LEGACY SDK COMPATIBILITY (PR #711): callers must provide exactly
|
|
1664
|
-
* one of this field or `windowSequence`. Make this the only allowed
|
|
1665
|
-
* field once legacy SDK support is dropped.
|
|
1666
|
-
*/
|
|
1667
|
-
windowStartedAtMs: number;
|
|
1668
|
-
windowSequence?: never;
|
|
1669
|
-
} | {
|
|
1670
|
-
windowStartedAtMs?: never;
|
|
1671
|
-
/**
|
|
1672
|
-
* LEGACY SDK COMPATIBILITY (PR #711).
|
|
1673
|
-
*
|
|
1674
|
-
* Monotonic window counter emitted by the pre-resumable-sessions
|
|
1675
|
-
* SDK. Remove this field once every deployed game has redeployed
|
|
1676
|
-
* against the post-#711 SDK + edge-play runtime.
|
|
1677
|
-
*/
|
|
1678
|
-
windowSequence: number;
|
|
1679
|
-
};
|
|
1680
|
-
type HeartbeatRequest = HeartbeatRequestBase & HeartbeatWindowKey;
|
|
1681
|
-
interface EndActivityRequest {
|
|
1274
|
+
interface AdjustTimebackTimeRequest {
|
|
1682
1275
|
gameId: string;
|
|
1276
|
+
courseId: string;
|
|
1683
1277
|
studentId: string;
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
* against the pre-resumable-sessions SDK do not send one. The
|
|
1690
|
-
* platform falls back to `runId`, or mints a server-side UUID, when
|
|
1691
|
-
* absent. Make this required once legacy SDK support is dropped.
|
|
1692
|
-
*/
|
|
1693
|
-
resumeId?: string;
|
|
1694
|
-
activityData: {
|
|
1695
|
-
activityId: string;
|
|
1696
|
-
activityName?: string;
|
|
1697
|
-
grade: TimebackGrade;
|
|
1698
|
-
subject: TimebackSubject;
|
|
1699
|
-
appName?: string;
|
|
1700
|
-
sensorUrl?: string;
|
|
1701
|
-
courseId?: string;
|
|
1702
|
-
courseName?: string;
|
|
1703
|
-
studentEmail?: string;
|
|
1704
|
-
};
|
|
1705
|
-
scoreData: {
|
|
1706
|
-
correctQuestions: number;
|
|
1707
|
-
totalQuestions: number;
|
|
1708
|
-
};
|
|
1709
|
-
timingData: {
|
|
1710
|
-
durationSeconds: number;
|
|
1711
|
-
};
|
|
1712
|
-
sessionTimingData?: {
|
|
1713
|
-
activeSeconds: number;
|
|
1714
|
-
inactiveSeconds?: number;
|
|
1715
|
-
};
|
|
1716
|
-
xpEarned: number;
|
|
1717
|
-
masteredUnits?: number;
|
|
1718
|
-
masteredUnitsAbsolute?: number;
|
|
1719
|
-
extensions?: TimebackActivityExtensions;
|
|
1278
|
+
seconds: number;
|
|
1279
|
+
reason: string;
|
|
1280
|
+
date?: string;
|
|
1281
|
+
/** See {@link GrantTimebackXpRequest.useCurrentTime}. */
|
|
1282
|
+
useCurrentTime?: boolean;
|
|
1720
1283
|
}
|
|
1721
|
-
interface
|
|
1722
|
-
|
|
1284
|
+
interface AdjustTimebackMasteryRequest {
|
|
1285
|
+
gameId: string;
|
|
1723
1286
|
courseId: string;
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
/**
|
|
1729
|
-
|
|
1730
|
-
* run (`runId`) and activity had already been recorded — the platform's
|
|
1731
|
-
* idempotency guard blocked a duplicate emit (PLA-142). This call awarded
|
|
1732
|
-
* nothing new, but the award may well have happened: if the original
|
|
1733
|
-
* response was lost in transit (e.g. a gateway timeout followed by an SDK
|
|
1734
|
-
* retry), this is the only response the caller ever sees for a run that
|
|
1735
|
-
* fully succeeded. The response therefore echoes what the run earned —
|
|
1736
|
-
* `xpAwarded`, `masteredUnits`, and `pctCompleteApp` are the values
|
|
1737
|
-
* recorded when the completion was confirmed (zeros, with no
|
|
1738
|
-
* `pctCompleteApp`, only for runs recorded before award echoing existed).
|
|
1739
|
-
* Treat it as "already recorded — here's what it earned", and do not
|
|
1740
|
-
* count the run again.
|
|
1741
|
-
*/
|
|
1742
|
-
blockedByDedupeGuard?: boolean;
|
|
1287
|
+
studentId: string;
|
|
1288
|
+
units: number;
|
|
1289
|
+
reason: string;
|
|
1290
|
+
date?: string;
|
|
1291
|
+
/** See {@link GrantTimebackXpRequest.useCurrentTime}. */
|
|
1292
|
+
useCurrentTime?: boolean;
|
|
1743
1293
|
}
|
|
1744
|
-
interface
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
highestGradeMastered: TimebackGrade | null;
|
|
1749
|
-
/** Normalized source grades returned by EduBridge for the rollup. */
|
|
1750
|
-
grades: {
|
|
1751
|
-
ritGrade: TimebackGrade | null;
|
|
1752
|
-
edulasticGrade: TimebackGrade | null;
|
|
1753
|
-
placementGrade: TimebackGrade | null;
|
|
1754
|
-
testOutGrade: TimebackGrade | null;
|
|
1755
|
-
highestGradeOverall: TimebackGrade | null;
|
|
1756
|
-
};
|
|
1294
|
+
interface EnrollStudentRequest {
|
|
1295
|
+
gameId: string;
|
|
1296
|
+
courseId: string;
|
|
1297
|
+
studentIds: string[];
|
|
1757
1298
|
}
|
|
1758
|
-
|
|
1759
|
-
* Highest-ranking OneRoster account role for a user shown in the admin
|
|
1760
|
-
* dashboard. Precedence: administrator > teacher > student. Accounts whose
|
|
1761
|
-
* roles fall outside those three (aide, guardian, …) resolve to `null`.
|
|
1762
|
-
*/
|
|
1763
|
-
type TimebackAccountRole = 'administrator' | 'teacher' | 'student';
|
|
1764
|
-
interface TimebackRosterStudent {
|
|
1299
|
+
interface EnrollStudentResult {
|
|
1765
1300
|
studentId: string;
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
className: string | null;
|
|
1769
|
-
name: string;
|
|
1770
|
-
email: string | null;
|
|
1771
|
-
primaryRole: TimebackAccountRole | null;
|
|
1772
|
-
analyticsUnavailable: boolean;
|
|
1773
|
-
totalXp: number;
|
|
1774
|
-
todayXp: number;
|
|
1775
|
-
activeTimeSeconds: number;
|
|
1776
|
-
masteredUnits: number;
|
|
1777
|
-
masterableUnits?: number;
|
|
1778
|
-
pctCompleteApp?: number;
|
|
1779
|
-
inactive?: boolean;
|
|
1301
|
+
status: 'ok' | 'already-enrolled' | 'not-found' | 'error';
|
|
1302
|
+
error?: string;
|
|
1780
1303
|
}
|
|
1781
|
-
interface
|
|
1304
|
+
interface EnrollStudentResponse {
|
|
1305
|
+
results: EnrollStudentResult[];
|
|
1306
|
+
}
|
|
1307
|
+
interface ReactivateEnrollmentRequest {
|
|
1782
1308
|
gameId: string;
|
|
1783
1309
|
courseId: string;
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
interface TimebackStudentHistoryPoint {
|
|
1787
|
-
date: string;
|
|
1788
|
-
xpEarned: number;
|
|
1789
|
-
activeTimeSeconds: number;
|
|
1790
|
-
masteredUnits: number;
|
|
1791
|
-
}
|
|
1792
|
-
type CourseCompletionStatus = 'none' | 'complete' | 'incomplete';
|
|
1793
|
-
interface TimebackStudentEnrollmentSummary {
|
|
1794
|
-
enrollmentId: string;
|
|
1795
|
-
status: 'active' | 'tobedeleted';
|
|
1796
|
-
beginDate: string | null;
|
|
1797
|
-
endDate: string | null;
|
|
1798
|
-
analyticsUnavailable: boolean;
|
|
1799
|
-
totalXp: number;
|
|
1800
|
-
todayXp: number;
|
|
1801
|
-
activeTimeSeconds: number;
|
|
1802
|
-
masteredUnits: number;
|
|
1803
|
-
pctCompleteApp?: number;
|
|
1804
|
-
history: TimebackStudentHistoryPoint[];
|
|
1805
|
-
}
|
|
1806
|
-
interface TimebackStudentCourseOverview {
|
|
1807
|
-
courseId: string;
|
|
1808
|
-
title: string;
|
|
1809
|
-
grade: number;
|
|
1810
|
-
subject: string;
|
|
1811
|
-
enrollmentId: string | null;
|
|
1812
|
-
analyticsUnavailable: boolean;
|
|
1813
|
-
totalXp: number;
|
|
1814
|
-
todayXp: number;
|
|
1815
|
-
activeTimeSeconds: number;
|
|
1816
|
-
masteredUnits: number;
|
|
1817
|
-
masterableUnits?: number;
|
|
1818
|
-
pctCompleteApp?: number;
|
|
1819
|
-
completionStatus: CourseCompletionStatus;
|
|
1820
|
-
history: TimebackStudentHistoryPoint[];
|
|
1821
|
-
inactive?: boolean;
|
|
1822
|
-
/** All enrollment records for this course, sorted active-first then by most recent. */
|
|
1823
|
-
enrollments?: TimebackStudentEnrollmentSummary[];
|
|
1824
|
-
}
|
|
1825
|
-
type TimebackRecentActivityKind = 'activity' | 'activity-in-progress' | 'time-spent' | 'remediation-xp' | 'remediation-time' | 'remediation-mastery' | 'course-completed' | 'course-resumed';
|
|
1826
|
-
interface TimebackRecentActivity {
|
|
1827
|
-
id: string;
|
|
1828
|
-
kind: TimebackRecentActivityKind;
|
|
1829
|
-
occurredAt: string;
|
|
1830
|
-
courseId: string;
|
|
1831
|
-
title: string;
|
|
1832
|
-
activityId?: string;
|
|
1833
|
-
appName?: string;
|
|
1834
|
-
reason?: string;
|
|
1835
|
-
score?: number;
|
|
1836
|
-
xpDelta?: number;
|
|
1837
|
-
timeDeltaSeconds?: number;
|
|
1838
|
-
masteredUnitsDelta?: number;
|
|
1839
|
-
runId?: string;
|
|
1840
|
-
sessionCount?: number;
|
|
1841
|
-
gameMetricsComparison?: GameRunMetricsComparisonSummary;
|
|
1842
|
-
gameMetricsVerification?: TimebackMetricDiscrepancyVerification;
|
|
1843
|
-
}
|
|
1844
|
-
interface TimebackStudentOverviewResponse {
|
|
1845
|
-
student: {
|
|
1846
|
-
studentId: string;
|
|
1847
|
-
name: string;
|
|
1848
|
-
email: string | null;
|
|
1849
|
-
};
|
|
1850
|
-
courses: TimebackStudentCourseOverview[];
|
|
1851
|
-
}
|
|
1852
|
-
interface TimebackStudentActivityResponse {
|
|
1853
|
-
activities: TimebackRecentActivity[];
|
|
1854
|
-
hasMore: boolean;
|
|
1855
|
-
}
|
|
1856
|
-
interface TimebackActivityDetailResponse {
|
|
1857
|
-
activity: TimebackRecentActivity;
|
|
1858
|
-
rawEvents: TimebackStoredCaliperEvent[];
|
|
1859
|
-
gameMetricsComparison?: GameRunMetricsComparison;
|
|
1860
|
-
}
|
|
1861
|
-
interface TimebackGradeLevelTestStudent {
|
|
1862
|
-
studentId: string;
|
|
1863
|
-
name: string;
|
|
1864
|
-
email: string | null;
|
|
1865
|
-
}
|
|
1866
|
-
interface TimebackGradeLevelTestStandardRef {
|
|
1867
|
-
source: string;
|
|
1868
|
-
id: string;
|
|
1869
|
-
identifier?: string;
|
|
1870
|
-
name?: string;
|
|
1871
|
-
description?: string;
|
|
1872
|
-
domain?: string;
|
|
1873
|
-
}
|
|
1874
|
-
interface TimebackGradeLevelTestStandardSummary extends TimebackGradeLevelTestStandardRef {
|
|
1875
|
-
attempted: number;
|
|
1876
|
-
missed: number;
|
|
1877
|
-
}
|
|
1878
|
-
interface TimebackGradeLevelTestSummary {
|
|
1879
|
-
resultId: string;
|
|
1880
|
-
lineItemId: string;
|
|
1881
|
-
studentId: string;
|
|
1882
|
-
testName: string;
|
|
1883
|
-
testType: string;
|
|
1884
|
-
provider: string;
|
|
1885
|
-
subject: string;
|
|
1886
|
-
grade: number | null;
|
|
1887
|
-
score: number | null;
|
|
1888
|
-
scorePercentile: number | null;
|
|
1889
|
-
scoreStatus: string;
|
|
1890
|
-
scoreDate: string | null;
|
|
1891
|
-
assignmentId?: string;
|
|
1892
|
-
qtiTestId?: string;
|
|
1893
|
-
questionCount?: number;
|
|
1894
|
-
correctQuestionCount?: number;
|
|
1895
|
-
}
|
|
1896
|
-
type TimebackGradeLevelQuestionCorrectness = 'correct' | 'incorrect' | 'partial' | 'unknown';
|
|
1897
|
-
interface TimebackGradeLevelTestQuestionResult {
|
|
1898
|
-
lineItemId: string;
|
|
1899
|
-
resultId: string | null;
|
|
1900
|
-
title: string;
|
|
1901
|
-
description?: string;
|
|
1902
|
-
correctness: TimebackGradeLevelQuestionCorrectness;
|
|
1903
|
-
score: number | null;
|
|
1904
|
-
scorePercentile: number | null;
|
|
1905
|
-
scoreStatus: string | null;
|
|
1906
|
-
standards: TimebackGradeLevelTestStandardRef[];
|
|
1907
|
-
qtiQuestion?: QtiTestQuestionRef | null;
|
|
1908
|
-
feedback?: string | null;
|
|
1909
|
-
}
|
|
1910
|
-
interface TimebackGradeLevelTestResultsResponse {
|
|
1911
|
-
gameId: string;
|
|
1912
|
-
courseId: string;
|
|
1913
|
-
subject: TimebackSubject;
|
|
1914
|
-
grade: TimebackGrade;
|
|
1915
|
-
student: TimebackGradeLevelTestStudent;
|
|
1916
|
-
highestGradeMastered: StudentHighestGradeMasteredResponse | null;
|
|
1917
|
-
results: TimebackGradeLevelTestSummary[];
|
|
1918
|
-
}
|
|
1919
|
-
interface TimebackGradeLevelTestReviewResponse {
|
|
1920
|
-
parentResultId: string;
|
|
1921
|
-
parentLineItemId: string;
|
|
1922
|
-
studentId: string;
|
|
1923
|
-
qtiQuestionCount: number;
|
|
1924
|
-
questions: TimebackGradeLevelTestQuestionResult[];
|
|
1925
|
-
standards: TimebackGradeLevelTestStandardSummary[];
|
|
1926
|
-
}
|
|
1927
|
-
type TimebackDiscrepancyQueueWindow = 'all' | 'today' | 'yesterday' | 'this-week' | 'last-week' | 'custom';
|
|
1928
|
-
interface TimebackDiscrepancyQueueDateRange {
|
|
1929
|
-
startDate?: string;
|
|
1930
|
-
endDate?: string;
|
|
1931
|
-
}
|
|
1932
|
-
interface TimebackMetricDiscrepancyVerification {
|
|
1933
|
-
gameId: string;
|
|
1934
|
-
courseId: string;
|
|
1935
|
-
studentId: string;
|
|
1936
|
-
runId: string;
|
|
1937
|
-
activityId?: string | null;
|
|
1938
|
-
verifiedAt: string;
|
|
1939
|
-
verifiedByUserId?: string | null;
|
|
1940
|
-
}
|
|
1941
|
-
interface TimebackDiscrepancyQueueStudent {
|
|
1942
|
-
studentId: string;
|
|
1943
|
-
name: string;
|
|
1944
|
-
email: string | null;
|
|
1945
|
-
}
|
|
1946
|
-
interface TimebackDiscrepancyQueueEventPage {
|
|
1947
|
-
offset: number;
|
|
1948
|
-
limit: number;
|
|
1949
|
-
total: number;
|
|
1950
|
-
hasPrevious: boolean;
|
|
1951
|
-
hasNext: boolean;
|
|
1952
|
-
}
|
|
1953
|
-
interface TimebackMetricDiscrepancyQueueItem {
|
|
1954
|
-
student: TimebackDiscrepancyQueueStudent;
|
|
1955
|
-
activity: TimebackRecentActivity;
|
|
1956
|
-
gameMetricsComparison: GameRunMetricsComparison;
|
|
1957
|
-
verification?: TimebackMetricDiscrepancyVerification;
|
|
1958
|
-
comparisonSource?: 'hydrated' | 'preliminary';
|
|
1959
|
-
}
|
|
1960
|
-
interface TimebackMetricDiscrepancyQueueResponse {
|
|
1961
|
-
gameId: string;
|
|
1962
|
-
courseId: string;
|
|
1963
|
-
window: TimebackDiscrepancyQueueWindow;
|
|
1964
|
-
dateRange: TimebackDiscrepancyQueueDateRange;
|
|
1965
|
-
studentId?: string;
|
|
1966
|
-
students: TimebackDiscrepancyQueueStudent[];
|
|
1967
|
-
discrepancyMetricScopes: GameMetricComparisonMetric[];
|
|
1968
|
-
eventPage: TimebackDiscrepancyQueueEventPage;
|
|
1969
|
-
includeVerified: boolean;
|
|
1970
|
-
items: TimebackMetricDiscrepancyQueueItem[];
|
|
1971
|
-
}
|
|
1972
|
-
interface VerifyTimebackMetricDiscrepancyRequest {
|
|
1973
|
-
studentId: string;
|
|
1974
|
-
runId: string;
|
|
1975
|
-
activityId?: string;
|
|
1976
|
-
}
|
|
1977
|
-
interface VerifyTimebackMetricDiscrepancyResponse {
|
|
1978
|
-
status: 'ok';
|
|
1979
|
-
verification: TimebackMetricDiscrepancyVerification;
|
|
1980
|
-
}
|
|
1981
|
-
interface GrantTimebackXpRequest {
|
|
1982
|
-
gameId: string;
|
|
1983
|
-
courseId: string;
|
|
1984
|
-
studentId: string;
|
|
1985
|
-
xp: number;
|
|
1986
|
-
reason: string;
|
|
1987
|
-
date?: string;
|
|
1988
|
-
useCurrentTime?: boolean;
|
|
1989
|
-
}
|
|
1990
|
-
interface AdjustTimebackTimeRequest {
|
|
1991
|
-
gameId: string;
|
|
1992
|
-
courseId: string;
|
|
1993
|
-
studentId: string;
|
|
1994
|
-
seconds: number;
|
|
1995
|
-
reason: string;
|
|
1996
|
-
date?: string;
|
|
1997
|
-
/** See {@link GrantTimebackXpRequest.useCurrentTime}. */
|
|
1998
|
-
useCurrentTime?: boolean;
|
|
1999
|
-
}
|
|
2000
|
-
interface AdjustTimebackMasteryRequest {
|
|
2001
|
-
gameId: string;
|
|
2002
|
-
courseId: string;
|
|
2003
|
-
studentId: string;
|
|
2004
|
-
units: number;
|
|
2005
|
-
reason: string;
|
|
2006
|
-
date?: string;
|
|
2007
|
-
/** See {@link GrantTimebackXpRequest.useCurrentTime}. */
|
|
2008
|
-
useCurrentTime?: boolean;
|
|
2009
|
-
}
|
|
2010
|
-
interface EnrollStudentRequest {
|
|
2011
|
-
gameId: string;
|
|
2012
|
-
courseId: string;
|
|
2013
|
-
studentIds: string[];
|
|
2014
|
-
}
|
|
2015
|
-
interface EnrollStudentResult {
|
|
2016
|
-
studentId: string;
|
|
2017
|
-
status: 'ok' | 'already-enrolled' | 'not-found' | 'error';
|
|
2018
|
-
error?: string;
|
|
2019
|
-
}
|
|
2020
|
-
interface EnrollStudentResponse {
|
|
2021
|
-
results: EnrollStudentResult[];
|
|
2022
|
-
}
|
|
2023
|
-
interface ReactivateEnrollmentRequest {
|
|
2024
|
-
gameId: string;
|
|
2025
|
-
courseId: string;
|
|
2026
|
-
studentId: string;
|
|
2027
|
-
enrollmentId: string;
|
|
1310
|
+
studentId: string;
|
|
1311
|
+
enrollmentId: string;
|
|
2028
1312
|
}
|
|
2029
1313
|
interface UnenrollStudentRequest {
|
|
2030
1314
|
gameId: string;
|
|
@@ -2891,230 +2175,1013 @@ interface StartAssessmentInputBase {
|
|
|
2891
2175
|
subject?: TimebackSubject;
|
|
2892
2176
|
grade?: TimebackGrade;
|
|
2893
2177
|
}
|
|
2894
|
-
interface StartFixedAssessmentInput extends StartAssessmentInputBase {
|
|
2895
|
-
purpose: Exclude<UnscopedFixedAssessmentPurpose, 'diagnostic'>;
|
|
2178
|
+
interface StartFixedAssessmentInput extends StartAssessmentInputBase {
|
|
2179
|
+
purpose: Exclude<UnscopedFixedAssessmentPurpose, 'diagnostic'>;
|
|
2180
|
+
}
|
|
2181
|
+
interface StartDiagnosticAssessmentInput extends StartAssessmentInputBase {
|
|
2182
|
+
purpose: 'diagnostic';
|
|
2183
|
+
/** Stable authored diagnostic identity, independent of the selected QTI test identifier. */
|
|
2184
|
+
diagnosticKey: string;
|
|
2185
|
+
}
|
|
2186
|
+
interface StartMasteryAssessmentInput extends StartAssessmentInputBase {
|
|
2187
|
+
purpose: 'mastery';
|
|
2188
|
+
standard: AssessmentStandardRef;
|
|
2189
|
+
}
|
|
2190
|
+
interface StartReviewAssessmentInput extends StartAssessmentInputBase {
|
|
2191
|
+
purpose: 'review';
|
|
2192
|
+
/** Serving order; duplicate canonical skills keep their first position. Pinned on start. */
|
|
2193
|
+
standards: AssessmentReviewStandard[];
|
|
2194
|
+
/** Candidate capacity per standard; preloaded candidates are not administered automatically. */
|
|
2195
|
+
candidateItemsPerStandard?: number;
|
|
2196
|
+
}
|
|
2197
|
+
type StartAssessmentInput = StartFixedAssessmentInput | StartDiagnosticAssessmentInput | StartMasteryAssessmentInput | StartReviewAssessmentInput;
|
|
2198
|
+
/** Transport metadata for attempting a prepared new-assessment start. */
|
|
2199
|
+
interface StartAssessmentOptions {
|
|
2200
|
+
preparationReceipt?: string;
|
|
2201
|
+
}
|
|
2202
|
+
/** Result of preparing the immediate next assessment request. */
|
|
2203
|
+
type AssessmentPreparationResult = {
|
|
2204
|
+
status: 'prepared';
|
|
2205
|
+
receipt: string;
|
|
2206
|
+
/** Receipt expiry as an ISO-8601 timestamp. */
|
|
2207
|
+
expiresAt: string;
|
|
2208
|
+
} | {
|
|
2209
|
+
status: 'unprepared';
|
|
2210
|
+
reason: 'artifact_unavailable' | 'prerequisite_unavailable' | 'local_runtime';
|
|
2211
|
+
};
|
|
2212
|
+
interface SaveAssessmentInput {
|
|
2213
|
+
expectedResponseVersion: number;
|
|
2214
|
+
/** Complete active draft; success acknowledges its durable parent checkpoint. */
|
|
2215
|
+
transcript: AssessmentTranscript;
|
|
2216
|
+
}
|
|
2217
|
+
/** Best-effort mastery progress, not an answer commitment or an explicit checkpoint. */
|
|
2218
|
+
interface RecordAssessmentProgressInput {
|
|
2219
|
+
assessmentToken: string;
|
|
2220
|
+
expectedResponseVersion: number;
|
|
2221
|
+
/** Current raw draft, including the editable administration being projected. */
|
|
2222
|
+
transcript: AssessmentTranscript;
|
|
2223
|
+
itemIdentifier: string;
|
|
2224
|
+
}
|
|
2225
|
+
interface SubmitAssessmentItemInputBase {
|
|
2226
|
+
assessmentToken: string;
|
|
2227
|
+
/** The raw prefix preceding this administration (or including it for an exact retry). */
|
|
2228
|
+
transcript: AssessmentTranscript;
|
|
2229
|
+
expectedResponseVersion: number;
|
|
2230
|
+
/** Stable across every retry of this exact item submission. */
|
|
2231
|
+
submissionId: string;
|
|
2232
|
+
itemIdentifier: string;
|
|
2233
|
+
responses: Record<string, AssessmentResponseValue | null>;
|
|
2234
|
+
}
|
|
2235
|
+
interface SubmitReviewAssessmentItemInput extends SubmitAssessmentItemInputBase {
|
|
2236
|
+
routingNodeKey?: never;
|
|
2237
|
+
}
|
|
2238
|
+
interface SubmitDiagnosticAssessmentItemInput extends SubmitAssessmentItemInputBase {
|
|
2239
|
+
/** Canonical routing-context identity returned in routing.next. */
|
|
2240
|
+
routingNodeKey: string;
|
|
2241
|
+
}
|
|
2242
|
+
type SubmitAssessmentItemInput = SubmitReviewAssessmentItemInput | SubmitDiagnosticAssessmentItemInput;
|
|
2243
|
+
interface SubmitReviewAssessmentItemResult {
|
|
2244
|
+
attemptId: string;
|
|
2245
|
+
responseVersion: number;
|
|
2246
|
+
status: 'in_progress';
|
|
2247
|
+
responses: AssessmentResponses;
|
|
2248
|
+
/** Canonical ledger after commit or replay; replace local state with it. */
|
|
2249
|
+
itemSubmissions: AssessmentItemSubmission[];
|
|
2250
|
+
submission: AssessmentItemSubmission;
|
|
2251
|
+
transcript: AssessmentTranscript;
|
|
2252
|
+
}
|
|
2253
|
+
/** Safe acknowledgement for one immutable diagnostic item commitment. */
|
|
2254
|
+
interface DiagnosticAssessmentItemReceipt {
|
|
2255
|
+
submissionId: string;
|
|
2256
|
+
routingNodeKey: string;
|
|
2257
|
+
itemIdentifier: string;
|
|
2258
|
+
submittedAt: string;
|
|
2259
|
+
responseVersion: number;
|
|
2260
|
+
answered: boolean;
|
|
2261
|
+
score: AssessmentScore;
|
|
2262
|
+
isCorrect: boolean;
|
|
2263
|
+
}
|
|
2264
|
+
/** Canonical post-commit routing state plus trusted feedback for this administration. */
|
|
2265
|
+
interface SubmitDiagnosticAssessmentItemResult {
|
|
2266
|
+
attemptId: string;
|
|
2267
|
+
responseVersion: number;
|
|
2268
|
+
status: 'in_progress';
|
|
2269
|
+
routing: DiagnosticRoutingSnapshot;
|
|
2270
|
+
submission: DiagnosticAssessmentItemReceipt;
|
|
2271
|
+
transcript: AssessmentTranscript;
|
|
2272
|
+
}
|
|
2273
|
+
type SubmitAssessmentItemResult = SubmitReviewAssessmentItemResult | SubmitDiagnosticAssessmentItemResult;
|
|
2274
|
+
interface SubmitAssessmentInput {
|
|
2275
|
+
expectedResponseVersion: number;
|
|
2276
|
+
submissionId: string;
|
|
2277
|
+
transcript: AssessmentTranscript;
|
|
2278
|
+
}
|
|
2279
|
+
/** The optional mastery portion of a finalize award; at most one field is set. */
|
|
2280
|
+
interface AssessmentMasteryAward {
|
|
2281
|
+
/**
|
|
2282
|
+
* Incremental learning units mastered by this attempt. Cannot be combined
|
|
2283
|
+
* with masteredUnitsAbsolute. The platform bounds the delta against the
|
|
2284
|
+
* course's configured masterableUnits and returns any warning.
|
|
2285
|
+
*/
|
|
2286
|
+
masteredUnits?: number;
|
|
2287
|
+
/**
|
|
2288
|
+
* Absolute mastered-unit total after this attempt; the platform computes
|
|
2289
|
+
* the delta. Cannot be combined with masteredUnits.
|
|
2290
|
+
*/
|
|
2291
|
+
masteredUnitsAbsolute?: number;
|
|
2292
|
+
}
|
|
2293
|
+
interface FinalizeAssessmentInput extends AssessmentMasteryAward {
|
|
2294
|
+
submissionId: string;
|
|
2295
|
+
/** Explicit zero is an award; omission is never interpreted as zero. */
|
|
2296
|
+
xpAwarded: number;
|
|
2297
|
+
}
|
|
2298
|
+
/**
|
|
2299
|
+
* The award a game supplied at finalization, retained verbatim so a retried
|
|
2300
|
+
* finalize can be told apart from a contradictory one even after bounding
|
|
2301
|
+
* changed what was actually applied.
|
|
2302
|
+
*/
|
|
2303
|
+
interface AssessmentAwardRecord extends AssessmentMasteryAward {
|
|
2304
|
+
xpAwarded: number;
|
|
2305
|
+
/** The bounded mastery delta the platform recorded for this attempt. */
|
|
2306
|
+
masteredUnitsApplied: number;
|
|
2307
|
+
pctCompleteApp?: number;
|
|
2308
|
+
warnings?: MasteryWriteWarning[];
|
|
2309
|
+
}
|
|
2310
|
+
/** One scored region for host-only point-response fixture scoring. */
|
|
2311
|
+
interface AssessmentResponseArea {
|
|
2312
|
+
shape: PlayableAssessmentShape;
|
|
2313
|
+
coords: number[];
|
|
2314
|
+
/** The region's mapped point value (1 when omitted). */
|
|
2315
|
+
value?: number;
|
|
2316
|
+
}
|
|
2317
|
+
/** One host-only assessment entry exported for the local runtime provider. */
|
|
2318
|
+
interface AssessmentRuntimeFixtureTest {
|
|
2319
|
+
id: string;
|
|
2320
|
+
assessmentKey: string;
|
|
2321
|
+
qtiTestIdentifier: string;
|
|
2322
|
+
live: boolean;
|
|
2323
|
+
/** Canonical quiz-level alignment used by mastery selection. */
|
|
2324
|
+
standard: AssessmentStandardRef | null;
|
|
2325
|
+
assessment: PlayableAssessment;
|
|
2326
|
+
/** Private comparison rules retained for hosted-equivalent local scoring when available. */
|
|
2327
|
+
scoring?: AssessmentScoringArtifactV1;
|
|
2328
|
+
/** Correct responses are retained by the host and are never returned to the game. */
|
|
2329
|
+
correctResponses: AssessmentResponses;
|
|
2330
|
+
/**
|
|
2331
|
+
* Host-only scored regions for point responses (select-point, position-object),
|
|
2332
|
+
* keyed by item then response identifier. A point response is correct when every
|
|
2333
|
+
* point lands in a distinct scored region.
|
|
2334
|
+
*/
|
|
2335
|
+
responseAreas?: Record<string, Record<string, AssessmentResponseArea[]>>;
|
|
2336
|
+
/** Host-only platform-routing sidecar for one immutable diagnostic bank revision. */
|
|
2337
|
+
diagnostic?: {
|
|
2338
|
+
diagnosticKey: string;
|
|
2339
|
+
/** Calculated from the canonical manifest when the fixture is built. */
|
|
2340
|
+
routingRevision: string;
|
|
2341
|
+
routingManifest: DiagnosticRoutingManifestV1;
|
|
2342
|
+
};
|
|
2343
|
+
/** Host-only standard index derived from this ordinary QTI test when used for review. */
|
|
2344
|
+
reviewBank?: {
|
|
2345
|
+
sourceContentRevision: string;
|
|
2346
|
+
bankRevision: string;
|
|
2347
|
+
items: {
|
|
2348
|
+
itemIdentifier: string;
|
|
2349
|
+
standards: AssessmentStandardRef[];
|
|
2350
|
+
}[];
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
/** Ordered local catalog for one course integration and assessment purpose. */
|
|
2354
|
+
interface AssessmentRuntimeFixtureCatalog {
|
|
2355
|
+
id: string;
|
|
2356
|
+
integrationId: string;
|
|
2357
|
+
purpose: AssessmentPurpose;
|
|
2358
|
+
subject: TimebackSubject;
|
|
2359
|
+
grade: TimebackGrade;
|
|
2360
|
+
tests: AssessmentRuntimeFixtureTest[];
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Versioned host-only bundle consumed by the local assessment runtime.
|
|
2364
|
+
*
|
|
2365
|
+
* The version tracks this bundle's own shape, including the shape of the
|
|
2366
|
+
* assessments nested inside it — a bundle is rejected outright when either
|
|
2367
|
+
* differs, so a stale export is re-generated rather than half-read.
|
|
2368
|
+
*/
|
|
2369
|
+
interface AssessmentRuntimeFixtureBundle {
|
|
2370
|
+
version: 8;
|
|
2371
|
+
gameId: string;
|
|
2372
|
+
/**
|
|
2373
|
+
* The project-config slug the CLI resolved the game by at export time.
|
|
2374
|
+
* Local dev matches on this (the platform gameId and the locally derived
|
|
2375
|
+
* id live in different namespaces, so they can never be compared).
|
|
2376
|
+
*/
|
|
2377
|
+
gameSlug?: string;
|
|
2378
|
+
exportedAt: string;
|
|
2379
|
+
catalogs: AssessmentRuntimeFixtureCatalog[];
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
/**
|
|
2383
|
+
* Must admit exactly the raster set in `QTI_RASTER_IMAGE_MIME_TYPES`
|
|
2384
|
+
* (`@playcademy/utils/timeback`), minus the legacy `image/jpg` spelling
|
|
2385
|
+
* the server never emits.
|
|
2386
|
+
*/
|
|
2387
|
+
type AssessmentAssetMimeType = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp' | 'image/bmp';
|
|
2388
|
+
type AssessmentAssetUploadStatus = 'uploaded' | 'already_exists';
|
|
2389
|
+
/** Assessment assets are production-oriented content; local sandboxes never host them. */
|
|
2390
|
+
type AssessmentAssetEnvironment = 'production' | 'staging';
|
|
2391
|
+
interface AssessmentAssetUploadResponse {
|
|
2392
|
+
status: AssessmentAssetUploadStatus;
|
|
2393
|
+
environment: AssessmentAssetEnvironment;
|
|
2394
|
+
key: string;
|
|
2395
|
+
url: string;
|
|
2396
|
+
sha256: string;
|
|
2397
|
+
mimeType: AssessmentAssetMimeType;
|
|
2398
|
+
size: number;
|
|
2399
|
+
width: number;
|
|
2400
|
+
height: number;
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
/**
|
|
2404
|
+
* User Types
|
|
2405
|
+
*
|
|
2406
|
+
* Enums, DTOs and API response types. Database row types are in @playcademy/data/types.
|
|
2407
|
+
*
|
|
2408
|
+
* @module types/user
|
|
2409
|
+
*/
|
|
2410
|
+
|
|
2411
|
+
type UserRoleEnumType = UserRole;
|
|
2412
|
+
type DeveloperStatusEnumType = 'none' | 'pending' | 'approved';
|
|
2413
|
+
type DeveloperStatusValue = DeveloperStatusEnumType;
|
|
2414
|
+
type TimebackUserRole = 'administrator' | 'aide' | 'guardian' | 'parent' | 'proctor' | 'relative' | 'student' | 'teacher';
|
|
2415
|
+
type TimebackOrgType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
2416
|
+
interface UserEnrollment {
|
|
2417
|
+
gameId?: string;
|
|
2418
|
+
courseId: string;
|
|
2419
|
+
id?: string;
|
|
2420
|
+
grade: number;
|
|
2421
|
+
subject: string;
|
|
2422
|
+
orgId?: string;
|
|
2423
|
+
/**
|
|
2424
|
+
* `base` for the game's grade courses, `hole_filling` for a remediation
|
|
2425
|
+
* course Timeback enrolled the student in. Absent means `base`.
|
|
2426
|
+
*/
|
|
2427
|
+
kind?: GameTimebackIntegrationKind;
|
|
2428
|
+
/** For hole-filling enrollments: the course ID of the base course it belongs to. */
|
|
2429
|
+
baseCourseId?: string;
|
|
2430
|
+
/** For hole-filling enrollments: when the enrollment started (ISO 8601). */
|
|
2431
|
+
beginDate?: string | null;
|
|
2432
|
+
/**
|
|
2433
|
+
* For hole-filling enrollments: enrollment metadata as set by Timeback
|
|
2434
|
+
* (e.g. `assignmentId`, `testType`). Every field is optional.
|
|
2435
|
+
*/
|
|
2436
|
+
enrollmentMetadata?: Record<string, unknown>;
|
|
2437
|
+
}
|
|
2438
|
+
interface UserOrganization {
|
|
2439
|
+
id: string;
|
|
2440
|
+
name: string | null;
|
|
2441
|
+
type: TimebackOrgType | string;
|
|
2442
|
+
isPrimary: boolean;
|
|
2443
|
+
}
|
|
2444
|
+
interface TimebackStudentProfile {
|
|
2445
|
+
role: TimebackUserRole;
|
|
2446
|
+
organizations: UserOrganization[];
|
|
2447
|
+
}
|
|
2448
|
+
/**
|
|
2449
|
+
* Timeback identity on `/users/me`: who the student is, without enrollments.
|
|
2450
|
+
* Enrollments come from `/timeback/user` (or `client.timeback.user`).
|
|
2451
|
+
*/
|
|
2452
|
+
interface UserTimebackProfile extends TimebackStudentProfile {
|
|
2453
|
+
id: string;
|
|
2454
|
+
}
|
|
2455
|
+
interface UserTimebackData extends UserTimebackProfile {
|
|
2456
|
+
enrollments: UserEnrollment[];
|
|
2457
|
+
}
|
|
2458
|
+
type LocalDaySource = 'browser_last_seen' | 'platform_default';
|
|
2459
|
+
interface LocalDayContext {
|
|
2460
|
+
timeZone: string;
|
|
2461
|
+
source: LocalDaySource;
|
|
2462
|
+
/** ISO 8601 timestamp for the last persisted timezone observation. */
|
|
2463
|
+
observedAt: string | null;
|
|
2464
|
+
}
|
|
2465
|
+
/**
|
|
2466
|
+
* OpenID Connect UserInfo claims (NOT a database row).
|
|
2467
|
+
*/
|
|
2468
|
+
interface UserInfo {
|
|
2469
|
+
sub: string;
|
|
2470
|
+
email: string;
|
|
2471
|
+
name: string | null;
|
|
2472
|
+
email_verified?: boolean;
|
|
2473
|
+
given_name?: string;
|
|
2474
|
+
family_name?: string;
|
|
2475
|
+
issuer?: string;
|
|
2476
|
+
lti_roles?: unknown;
|
|
2477
|
+
lti_context?: unknown;
|
|
2478
|
+
lti_resource_link?: unknown;
|
|
2479
|
+
timeback_id?: string;
|
|
2480
|
+
}
|
|
2481
|
+
interface DeveloperStatusResponse {
|
|
2482
|
+
status: DeveloperStatusEnumType;
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Authenticated user for API responses.
|
|
2486
|
+
* Differs from UserRow: omits timebackId, adds hasTimebackAccount and timeback.
|
|
2487
|
+
*/
|
|
2488
|
+
interface AuthenticatedUser {
|
|
2489
|
+
id: string;
|
|
2490
|
+
email: string;
|
|
2491
|
+
emailVerified: boolean;
|
|
2492
|
+
name: string | null;
|
|
2493
|
+
image: string | null;
|
|
2494
|
+
username: string | null;
|
|
2495
|
+
role: UserRoleEnumType;
|
|
2496
|
+
developerStatus: DeveloperStatusEnumType;
|
|
2497
|
+
createdAt: Date;
|
|
2498
|
+
updatedAt: Date;
|
|
2499
|
+
hasTimebackAccount: boolean;
|
|
2500
|
+
localDay: LocalDayContext;
|
|
2501
|
+
timeback?: UserTimebackProfile;
|
|
2502
|
+
}
|
|
2503
|
+
interface GameUser {
|
|
2504
|
+
id: string;
|
|
2505
|
+
name: string | null;
|
|
2506
|
+
role: UserRoleEnumType;
|
|
2507
|
+
username: string | null;
|
|
2508
|
+
email: string | null;
|
|
2509
|
+
localDay: LocalDayContext;
|
|
2510
|
+
timeback?: UserTimebackProfile;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
/**
|
|
2514
|
+
* Game Types
|
|
2515
|
+
*
|
|
2516
|
+
* Literal types and API DTOs. Database row types are in @playcademy/data/types.
|
|
2517
|
+
*
|
|
2518
|
+
* @module types/game
|
|
2519
|
+
*/
|
|
2520
|
+
|
|
2521
|
+
type GameType = 'hosted' | 'external';
|
|
2522
|
+
type GamePlatform = 'web' | 'godot' | (string & {});
|
|
2523
|
+
/**
|
|
2524
|
+
* Game manifest file format (manifest.json).
|
|
2525
|
+
* Note: createdAt is a string here because it's parsed from JSON file.
|
|
2526
|
+
*/
|
|
2527
|
+
interface ManifestV1 {
|
|
2528
|
+
version: '1';
|
|
2529
|
+
platform: string;
|
|
2530
|
+
createdAt: string;
|
|
2531
|
+
}
|
|
2532
|
+
interface ManifestVersions {
|
|
2533
|
+
vitePlugin?: string;
|
|
2534
|
+
sdk?: string;
|
|
2535
|
+
cli?: string;
|
|
2536
|
+
vite?: string;
|
|
2537
|
+
godotSdk?: string;
|
|
2538
|
+
godot?: string;
|
|
2539
|
+
}
|
|
2540
|
+
interface ManifestV2 {
|
|
2541
|
+
version: '2';
|
|
2542
|
+
platform: string;
|
|
2543
|
+
createdAt: string;
|
|
2544
|
+
gameVersion?: string;
|
|
2545
|
+
versions: ManifestVersions;
|
|
2546
|
+
}
|
|
2547
|
+
type GameManifest = ManifestV1 | ManifestV2;
|
|
2548
|
+
/** One migration in a migrate-mode deploy payload */
|
|
2549
|
+
interface DeployMigration {
|
|
2550
|
+
/** Journal tag, e.g. '0042_brave_hulk' */
|
|
2551
|
+
tag: string;
|
|
2552
|
+
/** Pre-split SQL statements (the CLI splits on drizzle statement-breakpoints) */
|
|
2553
|
+
statements: string[];
|
|
2554
|
+
/** sha-256 of the normalized migration SQL (sha256-v1) */
|
|
2555
|
+
checksum: string;
|
|
2556
|
+
}
|
|
2557
|
+
/** Push-mode database payload: diff-generated SQL guarded by a baseline CAS */
|
|
2558
|
+
interface DeployDatabasePush {
|
|
2559
|
+
mode: 'push';
|
|
2560
|
+
/** Diff-generated DDL to execute (semicolon-separated statements) */
|
|
2561
|
+
sql: string;
|
|
2562
|
+
/** Stored schemaHash the diff was computed against; null only for a first deploy */
|
|
2563
|
+
baselineHash: string | null;
|
|
2564
|
+
/** Drizzle snapshot JSON this deploy moves the schema to */
|
|
2565
|
+
nextSnapshot: unknown;
|
|
2566
|
+
/** Hash of nextSnapshot, stored as the new push baseline on success */
|
|
2567
|
+
nextHash: string;
|
|
2568
|
+
/** Explicit consent for destructive statements (DROP TABLE, dropped columns) */
|
|
2569
|
+
acceptDataLoss?: boolean;
|
|
2570
|
+
}
|
|
2571
|
+
/** Migrate-mode database payload: the client's complete migration journal */
|
|
2572
|
+
interface DeployDatabaseMigrate {
|
|
2573
|
+
mode: 'migrate';
|
|
2574
|
+
/** The full journal in order; the server applies whatever the ledger lacks */
|
|
2575
|
+
migrations: DeployMigration[];
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* Mode-aware database payload (PLA-217 D5). Replaces the legacy `schema`
|
|
2579
|
+
* field for adopted games; the server verifies state before executing.
|
|
2580
|
+
*/
|
|
2581
|
+
type DeployDatabasePayload = DeployDatabasePush | DeployDatabaseMigrate;
|
|
2582
|
+
/** Journal entry in a baseline adoption block */
|
|
2583
|
+
interface DeployBaselineJournalEntry {
|
|
2584
|
+
tag: string;
|
|
2585
|
+
checksum: string;
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Baseline adoption block (PLA-217 D7 seed-on-deploy): the client's claimed
|
|
2589
|
+
* starting state for a game that predates server-side deployment state.
|
|
2590
|
+
* Consumed by the deploy itself — an unadopted game gets its claimed ledger
|
|
2591
|
+
* rows, snapshot/hash, and live fingerprint recorded before the database
|
|
2592
|
+
* step runs; adopted games skip the block.
|
|
2593
|
+
*/
|
|
2594
|
+
interface DeployBaseline {
|
|
2595
|
+
lastAppliedMigrationTag?: string;
|
|
2596
|
+
journal?: DeployBaselineJournalEntry[];
|
|
2597
|
+
/**
|
|
2598
|
+
* Per-migration snapshot deltas for content validation (D7): the
|
|
2599
|
+
* seed claim is judged against the live database exactly like a
|
|
2600
|
+
* manual `db baseline` claim — the side door uses the front door's
|
|
2601
|
+
* checkpoint.
|
|
2602
|
+
*/
|
|
2603
|
+
evidence?: BaselineMigrationEvidence[];
|
|
2604
|
+
schemaSnapshot?: unknown;
|
|
2605
|
+
schemaHash?: string;
|
|
2606
|
+
integrationsHash?: string;
|
|
2607
|
+
buildHash?: string;
|
|
2608
|
+
}
|
|
2609
|
+
/**
|
|
2610
|
+
* Git provenance of the working tree a deploy was run from (PLA-294).
|
|
2611
|
+
*
|
|
2612
|
+
* Captured by the CLI at deploy time and persisted verbatim on the deploy
|
|
2613
|
+
* job, so "which commit is running" is answerable from the platform record.
|
|
2614
|
+
* Every field is nullable: a tree with no git, no remote, or a detached HEAD
|
|
2615
|
+
* is recorded honestly rather than refused. Recorded per deploy, never
|
|
2616
|
+
* pinned to the game — a fork deployed under the same slug shows the fork.
|
|
2617
|
+
*/
|
|
2618
|
+
interface DeploySource {
|
|
2619
|
+
/** Normalized `https://github.com/<org>/<repo>`; null when no remote or unparseable */
|
|
2620
|
+
repository: string | null;
|
|
2621
|
+
/** Full 40-char SHA of HEAD; null when not in a git tree */
|
|
2622
|
+
commit: string | null;
|
|
2623
|
+
/** Current branch name; null on detached HEAD outside CI */
|
|
2624
|
+
branch: string | null;
|
|
2625
|
+
/** True when the tree had uncommitted changes — the SHA does not describe what shipped */
|
|
2626
|
+
dirty: boolean;
|
|
2627
|
+
/**
|
|
2628
|
+
* True when the uploaded build artifact lives outside the git tree the
|
|
2629
|
+
* other fields describe (`--build ~/Downloads/game.zip`). The commit is
|
|
2630
|
+
* still the deployer's tree, but it may not be what produced the artifact.
|
|
2631
|
+
*/
|
|
2632
|
+
externalBuild: boolean;
|
|
2633
|
+
}
|
|
2634
|
+
type DeployJobStatus = 'pending' | 'running' | 'succeeded' | 'failed';
|
|
2635
|
+
/** Active game worker state served by the deployment-state endpoint */
|
|
2636
|
+
interface DeploymentStateGame {
|
|
2637
|
+
/** Backend bundle hash from the active deployment row */
|
|
2638
|
+
codeHash: string | null;
|
|
2639
|
+
/** Frontend zip hash from game_deployment_state (null when unseeded) */
|
|
2640
|
+
buildHash: string | null;
|
|
2641
|
+
url: string;
|
|
2642
|
+
deployedAt: string;
|
|
2643
|
+
}
|
|
2644
|
+
/** Active dashboard worker state served by the deployment-state endpoint */
|
|
2645
|
+
interface DeploymentStateDashboard {
|
|
2646
|
+
url: string;
|
|
2647
|
+
deployedAt: string;
|
|
2648
|
+
}
|
|
2649
|
+
/** Details of the last failed migration, from the failed deploy job */
|
|
2650
|
+
interface DeploymentStateDatabaseFailure {
|
|
2651
|
+
tag: string;
|
|
2652
|
+
error: string;
|
|
2653
|
+
/**
|
|
2654
|
+
* Set only when the failing statement is known exactly (the size
|
|
2655
|
+
* preflight names the statement it measured). Execution failures are
|
|
2656
|
+
* null: D1's byte offset has an unverified reference frame for the
|
|
2657
|
+
* batch array form, so it is never mapped to an index.
|
|
2658
|
+
*/
|
|
2659
|
+
statementIndex: number | null;
|
|
2660
|
+
at: string;
|
|
2661
|
+
}
|
|
2662
|
+
/** A ledger entry from the game database's applied-migrations ledger */
|
|
2663
|
+
interface DeploymentStateAppliedMigration {
|
|
2664
|
+
tag: string;
|
|
2665
|
+
/** Checksum recorded at apply time (normalized SQL, see checksumAlgo) */
|
|
2666
|
+
checksum: string;
|
|
2667
|
+
/** Hash algorithm the checksum was recorded under (e.g. 'sha256-v1') */
|
|
2668
|
+
checksumAlgo: string;
|
|
2669
|
+
}
|
|
2670
|
+
/** Database schema state served by the deployment-state endpoint */
|
|
2671
|
+
interface DeploymentStateDatabase {
|
|
2672
|
+
/** Applied migrations from the live in-D1 ledger (null when the game has no database) */
|
|
2673
|
+
appliedMigrations: DeploymentStateAppliedMigration[] | null;
|
|
2674
|
+
lastFailure: DeploymentStateDatabaseFailure | null;
|
|
2675
|
+
/** Hash of the drizzle snapshot (push-mode baseline) */
|
|
2676
|
+
schemaHash: string | null;
|
|
2677
|
+
/** Hash of the normalized live sqlite_master */
|
|
2678
|
+
schemaFingerprint: string | null;
|
|
2679
|
+
/**
|
|
2680
|
+
* Stored drizzle snapshot (push-mode diffing only). Present only when
|
|
2681
|
+
* the request asked for it with `?include=schemaSnapshot` (D4).
|
|
2682
|
+
*/
|
|
2683
|
+
schemaSnapshot?: unknown;
|
|
2684
|
+
}
|
|
2685
|
+
/** Secret key names partitioned by ownership — names only, never hashes */
|
|
2686
|
+
interface DeploymentStateSecrets {
|
|
2687
|
+
/** Keys recorded in the server-side secrets manifest */
|
|
2688
|
+
managedKeys: string[];
|
|
2689
|
+
/**
|
|
2690
|
+
* Remote keys not in the manifest (set by SST, wrangler, or hand).
|
|
2691
|
+
* Null when the server has no Cloudflare provider to list remote keys.
|
|
2692
|
+
*/
|
|
2693
|
+
unmanagedKeys: string[] | null;
|
|
2694
|
+
}
|
|
2695
|
+
interface DeploymentStateLastDeploy {
|
|
2696
|
+
at: string;
|
|
2697
|
+
/** Who deployed: their email, or the raw user id when the account no longer resolves */
|
|
2698
|
+
by: string;
|
|
2699
|
+
}
|
|
2700
|
+
/**
|
|
2701
|
+
* Server-side deployment state for a game.
|
|
2702
|
+
*
|
|
2703
|
+
* Everything CLI deploy prep diffs against, fetched from
|
|
2704
|
+
* GET /api/games/:slug/deployment-state so deploys never depend on
|
|
2705
|
+
* local state for correctness.
|
|
2706
|
+
*/
|
|
2707
|
+
interface DeploymentStateResponse {
|
|
2708
|
+
gameId: string;
|
|
2709
|
+
/** True iff a game_deployment_state row exists for the game */
|
|
2710
|
+
seeded: boolean;
|
|
2711
|
+
game: DeploymentStateGame | null;
|
|
2712
|
+
dashboard: DeploymentStateDashboard | null;
|
|
2713
|
+
database: DeploymentStateDatabase;
|
|
2714
|
+
secrets: DeploymentStateSecrets;
|
|
2715
|
+
/** Hash of the declared integrations config (null when unseeded) */
|
|
2716
|
+
integrationsHash: string | null;
|
|
2717
|
+
/** Last deployed compatibility date (null when unseeded) */
|
|
2718
|
+
compatibilityDate: string | null;
|
|
2719
|
+
lastDeploy: DeploymentStateLastDeploy | null;
|
|
2720
|
+
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Per-key verdict groups from the server-side secrets diff (D6).
|
|
2723
|
+
*
|
|
2724
|
+
* Key names only — HMAC digests never leave the server. `remoteOnlyManaged`
|
|
2725
|
+
* keys are the only legal prune candidates; `remoteOnlyUnmanaged` keys were
|
|
2726
|
+
* set outside the platform and are untouchable.
|
|
2727
|
+
*/
|
|
2728
|
+
interface SecretsDiffResponse {
|
|
2729
|
+
/** Local keys the manifest doesn't know yet */
|
|
2730
|
+
added: string[];
|
|
2731
|
+
/** Keys whose local value's HMAC differs from the manifest's */
|
|
2732
|
+
changed: string[];
|
|
2733
|
+
/** Keys whose local value's HMAC matches the manifest's */
|
|
2734
|
+
unchanged: string[];
|
|
2735
|
+
/** Manifest keys absent locally (prunable) */
|
|
2736
|
+
remoteOnlyManaged: string[];
|
|
2737
|
+
/** Worker keys absent from both the manifest and the local set */
|
|
2738
|
+
remoteOnlyUnmanaged: string[];
|
|
2739
|
+
}
|
|
2740
|
+
/**
|
|
2741
|
+
* Manual baseline adoption request (PLA-217 D7): the fresh-machine path for
|
|
2742
|
+
* a game that predates server-side deployment state. Carries the claimed
|
|
2743
|
+
* migrate-mode history (tag + journal), the push-mode snapshot, or both.
|
|
2744
|
+
*/
|
|
2745
|
+
interface DeploymentStateBaselineRequest {
|
|
2746
|
+
lastAppliedMigrationTag?: string;
|
|
2747
|
+
journal?: DeployBaselineJournalEntry[];
|
|
2748
|
+
schemaSnapshot?: unknown;
|
|
2749
|
+
schemaHash?: string;
|
|
2750
|
+
/**
|
|
2751
|
+
* Per-migration schema-object evidence (creates AND drops) derived
|
|
2752
|
+
* from drizzle's snapshot files. The server replays it up to the
|
|
2753
|
+
* claimed tag into the expected schema and validates that against the
|
|
2754
|
+
* live database (D7): surviving claimed objects must exist,
|
|
2755
|
+
* beyond-claim objects must be absent unless claimed history already
|
|
2756
|
+
* explains them. Absent evidence downgrades every migration to
|
|
2757
|
+
* no-signal.
|
|
2758
|
+
*/
|
|
2759
|
+
evidence?: BaselineMigrationEvidence[];
|
|
2760
|
+
/**
|
|
2761
|
+
* Waive refusals for no-signal migrations only (content contradictions
|
|
2762
|
+
* are never waivable). Recorded server-side for audit.
|
|
2763
|
+
*/
|
|
2764
|
+
allowUnverified?: boolean;
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Schema objects one journal migration introduced and removed (snapshot
|
|
2768
|
+
* delta). Drops are as load-bearing as creates: an object created by one
|
|
2769
|
+
* migration and dropped by a later one must not be demanded of the live
|
|
2770
|
+
* database, and its live presence beyond the claim must not convict a
|
|
2771
|
+
* migration whose object is already explained by claimed history.
|
|
2772
|
+
*/
|
|
2773
|
+
interface BaselineMigrationEvidence {
|
|
2774
|
+
tag: string;
|
|
2775
|
+
/** Journal generation timestamp (drizzle `when`), ISO 8601 */
|
|
2776
|
+
generatedAt: string;
|
|
2777
|
+
createsTables: {
|
|
2778
|
+
name: string;
|
|
2779
|
+
columns: string[];
|
|
2780
|
+
}[];
|
|
2781
|
+
addsColumns: {
|
|
2782
|
+
table: string;
|
|
2783
|
+
column: string;
|
|
2784
|
+
}[];
|
|
2785
|
+
createsIndexes: string[];
|
|
2786
|
+
createsViews: string[];
|
|
2787
|
+
dropsTables: string[];
|
|
2788
|
+
dropsColumns: {
|
|
2789
|
+
table: string;
|
|
2790
|
+
column: string;
|
|
2791
|
+
}[];
|
|
2792
|
+
dropsIndexes: string[];
|
|
2793
|
+
dropsViews: string[];
|
|
2794
|
+
}
|
|
2795
|
+
/** Outcome of the `db realign <tag>` checksum pardon */
|
|
2796
|
+
interface MigrationRealignResponse {
|
|
2797
|
+
tag: string;
|
|
2798
|
+
/** The re-recorded checksum */
|
|
2799
|
+
checksum: string;
|
|
2800
|
+
/** Algorithm stamped alongside it (e.g. 'sha256-v1') */
|
|
2801
|
+
checksumAlgo: string;
|
|
2802
|
+
}
|
|
2803
|
+
/** How a ledger escape-hatch resolution lands: record the row, or remove it */
|
|
2804
|
+
type MigrationResolution = 'applied' | 'rolled-back';
|
|
2805
|
+
/** Outcome of the `db resolve <tag>` ledger escape hatch */
|
|
2806
|
+
interface MigrationResolveResponse {
|
|
2807
|
+
tag: string;
|
|
2808
|
+
resolution: MigrationResolution;
|
|
2809
|
+
/**
|
|
2810
|
+
* Live schema fingerprint captured by the resolve: the developer's
|
|
2811
|
+
* attestation that the current database state is intended, so the next
|
|
2812
|
+
* deploy verifies against it instead of the pre-repair record.
|
|
2813
|
+
*/
|
|
2814
|
+
schemaFingerprint: string;
|
|
2815
|
+
/**
|
|
2816
|
+
* Whether the fingerprint was persisted to the game's deployment-state
|
|
2817
|
+
* row. False when the game has no such row (not yet adopted): the
|
|
2818
|
+
* ledger repair still succeeded, but nothing was recorded.
|
|
2819
|
+
*/
|
|
2820
|
+
fingerprintRecorded: boolean;
|
|
2821
|
+
}
|
|
2822
|
+
/** One deploy job in a game's execution history (PLA-217 D10) */
|
|
2823
|
+
interface GameDeployHistoryDeploy {
|
|
2824
|
+
kind: 'deploy';
|
|
2825
|
+
status: DeployJobStatus;
|
|
2826
|
+
/** When the deploy landed (creation time while pending/running), ISO 8601 */
|
|
2827
|
+
at: string;
|
|
2828
|
+
/** When the job finished (null while pending/running) */
|
|
2829
|
+
completedAt: string | null;
|
|
2830
|
+
/** Who deployed: their email, or DELETED_ACCOUNT_LABEL when the account is gone */
|
|
2831
|
+
by: string;
|
|
2832
|
+
/** Client idempotency key (null for legacy clients) */
|
|
2833
|
+
deployId: string | null;
|
|
2834
|
+
error: string | null;
|
|
2835
|
+
}
|
|
2836
|
+
/** A Time Travel restore in a game's execution history */
|
|
2837
|
+
interface GameDeployHistoryRestore {
|
|
2838
|
+
kind: 'restore';
|
|
2839
|
+
/** When the restore ran, ISO 8601 */
|
|
2840
|
+
at: string;
|
|
2841
|
+
/** Who restored: their email, or DELETED_ACCOUNT_LABEL when the account is gone */
|
|
2842
|
+
by: string;
|
|
2843
|
+
/** The moment the database was rewound to, ISO 8601 */
|
|
2844
|
+
restoredTo: string;
|
|
2896
2845
|
}
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2846
|
+
/** A client-gate refusal in a game's execution history */
|
|
2847
|
+
interface GameDeployHistoryBlocked {
|
|
2848
|
+
kind: 'blocked';
|
|
2849
|
+
/** When the gate refused, ISO 8601 */
|
|
2850
|
+
at: string;
|
|
2851
|
+
/** Whose deploy was refused: their email, or DELETED_ACCOUNT_LABEL */
|
|
2852
|
+
by: string;
|
|
2853
|
+
/** Stable `blocked:*` code the gate refused with */
|
|
2854
|
+
code: string;
|
|
2855
|
+
/** The gate's plain-text refusal reason */
|
|
2856
|
+
reason: string;
|
|
2901
2857
|
}
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2858
|
+
/**
|
|
2859
|
+
* One entry in a game's execution history: deploy jobs interleaved with
|
|
2860
|
+
* the platform-visible events the job system never sees (restores,
|
|
2861
|
+
* client-gate refusals), newest first.
|
|
2862
|
+
*/
|
|
2863
|
+
type GameDeployHistoryEntry = GameDeployHistoryDeploy | GameDeployHistoryRestore | GameDeployHistoryBlocked;
|
|
2864
|
+
/** Deploy history served by GET /api/games/:slug/deployments */
|
|
2865
|
+
interface GameDeployHistoryResponse {
|
|
2866
|
+
deploys: GameDeployHistoryEntry[];
|
|
2905
2867
|
}
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2868
|
+
/**
|
|
2869
|
+
* One successful deploy of a game's worker, as the open releases surface
|
|
2870
|
+
* describes it: what is running and which commit produced it.
|
|
2871
|
+
*
|
|
2872
|
+
* This shape is parsed by external automation. Every field is present on
|
|
2873
|
+
* every entry — nullable, never absent — and nothing from the deploy job
|
|
2874
|
+
* beyond its git provenance and completion time is exposed. No deployer
|
|
2875
|
+
* identity, no schema state, no secrets manifest.
|
|
2876
|
+
*/
|
|
2877
|
+
interface Release {
|
|
2878
|
+
slug: string;
|
|
2879
|
+
/** Where the game is served; null when the platform has no URL for it */
|
|
2880
|
+
url: string | null;
|
|
2881
|
+
/**
|
|
2882
|
+
* Git provenance the deploying CLI captured. Null for deploys that
|
|
2883
|
+
* predate provenance capture, so the fleet list is complete on day one
|
|
2884
|
+
* and a consumer can tell "unknown" from "not in git".
|
|
2885
|
+
*/
|
|
2886
|
+
source: DeploySource | null;
|
|
2887
|
+
/** When the deploy succeeded, ISO 8601 */
|
|
2888
|
+
deployedAt: string;
|
|
2912
2889
|
}
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
preparationReceipt?: string;
|
|
2890
|
+
/** GET /api/releases — the current release of every game */
|
|
2891
|
+
interface ReleasesResponse {
|
|
2892
|
+
releases: Release[];
|
|
2917
2893
|
}
|
|
2918
|
-
/**
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
/**
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
};
|
|
2928
|
-
interface SaveAssessmentInput {
|
|
2929
|
-
expectedResponseVersion: number;
|
|
2930
|
-
/** Complete active draft; success acknowledges its durable parent checkpoint. */
|
|
2931
|
-
transcript: AssessmentTranscript;
|
|
2894
|
+
/** GET /api/releases/:slug — one game's current release and, on request, its recent history */
|
|
2895
|
+
interface GameReleaseResponse {
|
|
2896
|
+
/** The current release; null when the game has never deployed successfully */
|
|
2897
|
+
current: Release | null;
|
|
2898
|
+
/**
|
|
2899
|
+
* The last N successful deploys, newest first, current included. Only
|
|
2900
|
+
* present when `?history=N` was requested.
|
|
2901
|
+
*/
|
|
2902
|
+
history?: Release[];
|
|
2932
2903
|
}
|
|
2933
|
-
/**
|
|
2934
|
-
interface
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
/**
|
|
2938
|
-
|
|
2939
|
-
itemIdentifier: string;
|
|
2904
|
+
/** Request body for POST /api/games/:slug/deployments/blocked */
|
|
2905
|
+
interface DeployBlockedReport {
|
|
2906
|
+
/** Stable `blocked:*` code the gate refused with */
|
|
2907
|
+
code: string;
|
|
2908
|
+
/** The gate's plain-text refusal reason */
|
|
2909
|
+
reason: string;
|
|
2940
2910
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2911
|
+
/**
|
|
2912
|
+
* A restorable pre-deploy database state (D1 Time Travel bookmark).
|
|
2913
|
+
*
|
|
2914
|
+
* Each schema-bearing deploy captures a bookmark of the database as it was
|
|
2915
|
+
* the moment before that deploy's schema changes ran. Only bookmarks
|
|
2916
|
+
* belonging to the CURRENT database and within Time Travel's retention
|
|
2917
|
+
* window are served — a `db reset` creates a new database (bookmarks do
|
|
2918
|
+
* not cross that boundary), and Cloudflare expires bookmarks after the
|
|
2919
|
+
* account plan's retention period.
|
|
2920
|
+
*/
|
|
2921
|
+
interface GameRestorePoint {
|
|
2922
|
+
/** Deployment row id — pass to the restore endpoint (or a unique prefix to the CLI) */
|
|
2923
|
+
id: string;
|
|
2924
|
+
/**
|
|
2925
|
+
* When the bookmark was captured (the exact state a restore rewinds
|
|
2926
|
+
* to), ISO 8601. Rows from before capture times were recorded fall
|
|
2927
|
+
* back to the deploy completion time.
|
|
2928
|
+
*/
|
|
2929
|
+
capturedAt: string;
|
|
2930
|
+
/** Whether the capturing deployment is the currently active one */
|
|
2931
|
+
active: boolean;
|
|
2950
2932
|
}
|
|
2951
|
-
|
|
2952
|
-
|
|
2933
|
+
/** Restore points served by GET /api/games/:slug/database/restore-points */
|
|
2934
|
+
interface GameRestorePointsResponse {
|
|
2935
|
+
restorePoints: GameRestorePoint[];
|
|
2936
|
+
/**
|
|
2937
|
+
* True for push-adopted games, whose recorded snapshot cannot rewind
|
|
2938
|
+
* with the data: restore is categorically refused, so the list is
|
|
2939
|
+
* served empty rather than offering points that can never restore.
|
|
2940
|
+
*/
|
|
2941
|
+
restoreUnsupported: boolean;
|
|
2953
2942
|
}
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2943
|
+
/** Outcome of a Time Travel restore (PLA-217 D10) */
|
|
2944
|
+
interface DatabaseRestoreResponse {
|
|
2945
|
+
restorePointId: string;
|
|
2946
|
+
/** The moment the database was rewound to (the bookmark's capture time), ISO 8601 */
|
|
2947
|
+
restoredTo: string;
|
|
2948
|
+
/**
|
|
2949
|
+
* Live schema fingerprint captured after the restore: the developer's
|
|
2950
|
+
* audited attestation that the rewound state is intended, so the next
|
|
2951
|
+
* deploy verifies against it.
|
|
2952
|
+
*/
|
|
2953
|
+
schemaFingerprint: string;
|
|
2954
|
+
/** False when the game has no deployment-state row to re-attest */
|
|
2955
|
+
fingerprintRecorded: boolean;
|
|
2956
|
+
/**
|
|
2957
|
+
* Bookmark of the database as it was just before this restore — the
|
|
2958
|
+
* undo handle. Null only if Cloudflare omits it from the response.
|
|
2959
|
+
*/
|
|
2960
|
+
previousBookmark: string | null;
|
|
2957
2961
|
}
|
|
2958
|
-
|
|
2959
|
-
interface
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
transcript: AssessmentTranscript;
|
|
2962
|
+
/** Push-mode reset payload: the full schema that rebuilds the fresh database */
|
|
2963
|
+
interface DatabaseResetPush {
|
|
2964
|
+
mode: 'push';
|
|
2965
|
+
/** Full schema DDL (semicolon-separated statements) */
|
|
2966
|
+
sql: string;
|
|
2967
|
+
/** Drizzle snapshot JSON, stored as the new push baseline */
|
|
2968
|
+
nextSnapshot: unknown;
|
|
2969
|
+
/** Hash of nextSnapshot, stored as the new schemaHash */
|
|
2970
|
+
nextHash: string;
|
|
2968
2971
|
}
|
|
2969
|
-
/**
|
|
2970
|
-
interface
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
submittedAt: string;
|
|
2975
|
-
responseVersion: number;
|
|
2976
|
-
answered: boolean;
|
|
2977
|
-
score: AssessmentScore;
|
|
2978
|
-
isCorrect: boolean;
|
|
2972
|
+
/** Migrate-mode reset payload: the complete journal to replay on the fresh database */
|
|
2973
|
+
interface DatabaseResetMigrate {
|
|
2974
|
+
mode: 'migrate';
|
|
2975
|
+
/** The full journal in order; replayed one-by-one, rebuilding the ledger */
|
|
2976
|
+
migrations: DeployMigration[];
|
|
2979
2977
|
}
|
|
2980
|
-
/**
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2978
|
+
/**
|
|
2979
|
+
* Mode-aware reset payload (PLA-217 D2). Reset recreates the database, so
|
|
2980
|
+
* the ledger dies with it and there is no baseline to compare against —
|
|
2981
|
+
* adopted games must send how to rebuild.
|
|
2982
|
+
*/
|
|
2983
|
+
type DatabaseResetDatabasePayload = DatabaseResetPush | DatabaseResetMigrate;
|
|
2984
|
+
interface DatabaseResetRequest {
|
|
2985
|
+
/**
|
|
2986
|
+
* Legacy schema info, accepted only while the game's schema state is
|
|
2987
|
+
* unadopted; adopted games must send `database` instead.
|
|
2988
|
+
*/
|
|
2989
|
+
schema?: {
|
|
2990
|
+
/** SQL schema statements (DDL: CREATE, ALTER, etc.) */
|
|
2991
|
+
sql: string;
|
|
2992
|
+
/** Hash of the schema for change detection */
|
|
2993
|
+
hash: string;
|
|
2994
|
+
};
|
|
2995
|
+
/** Mode-aware rebuild payload (mandatory for adopted games) */
|
|
2996
|
+
database?: DatabaseResetDatabasePayload;
|
|
2988
2997
|
}
|
|
2989
|
-
|
|
2990
|
-
interface
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2998
|
+
/** Log entry captured from seed worker console output */
|
|
2999
|
+
interface SeedLogEntry {
|
|
3000
|
+
/** Log level (log, warn, error, info) */
|
|
3001
|
+
level: 'log' | 'warn' | 'error' | 'info';
|
|
3002
|
+
/** Log message content */
|
|
3003
|
+
message: string;
|
|
3004
|
+
/** Milliseconds since seed execution started */
|
|
3005
|
+
timestamp: number;
|
|
3006
|
+
}
|
|
3007
|
+
/** Structured error details from D1/SQLite errors */
|
|
3008
|
+
interface SeedErrorDetails {
|
|
3009
|
+
/** Error category code */
|
|
3010
|
+
code?: 'CONSTRAINT_VIOLATION' | 'SQL_ERROR' | 'DATABASE_BUSY';
|
|
3011
|
+
/** Table name involved in the error */
|
|
3012
|
+
table?: string;
|
|
3013
|
+
/** Constraint name or column that caused the error */
|
|
3014
|
+
constraint?: string;
|
|
3015
|
+
/** Specific constraint type */
|
|
3016
|
+
constraintType?: 'UNIQUE' | 'FOREIGN_KEY' | 'NOT_NULL';
|
|
3017
|
+
/** Token near syntax error */
|
|
3018
|
+
nearToken?: string;
|
|
3019
|
+
/** Specific error type within category */
|
|
3020
|
+
errorType?: 'TABLE_NOT_FOUND' | 'SYNTAX_ERROR';
|
|
2994
3021
|
}
|
|
2995
|
-
/**
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
/**
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3022
|
+
/**
|
|
3023
|
+
* API response for seed operations (what the API returns to the CLI).
|
|
3024
|
+
*
|
|
3025
|
+
* Extends the worker response with deployment metadata.
|
|
3026
|
+
*/
|
|
3027
|
+
interface SeedResponse {
|
|
3028
|
+
/** Whether the seed completed successfully */
|
|
3029
|
+
success: boolean;
|
|
3030
|
+
/** Unique identifier for the seed worker deployment */
|
|
3031
|
+
deploymentId: string;
|
|
3032
|
+
/** When the seed was executed (ISO 8601 string from JSON serialization) */
|
|
3033
|
+
executedAt: string;
|
|
3034
|
+
/** Captured console output from the seed script */
|
|
3035
|
+
logs?: SeedLogEntry[];
|
|
3036
|
+
/** Execution duration in milliseconds */
|
|
3037
|
+
duration?: number;
|
|
3038
|
+
/** Error message if seed failed */
|
|
3039
|
+
error?: string;
|
|
3040
|
+
/** Stack trace if seed failed */
|
|
3041
|
+
stack?: string;
|
|
3042
|
+
/** Structured error details if seed failed */
|
|
3043
|
+
details?: SeedErrorDetails;
|
|
3008
3044
|
}
|
|
3009
|
-
interface
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3045
|
+
interface DomainValidationRecords {
|
|
3046
|
+
ownership?: {
|
|
3047
|
+
name?: string;
|
|
3048
|
+
value?: string;
|
|
3049
|
+
type?: string;
|
|
3050
|
+
};
|
|
3051
|
+
ssl?: {
|
|
3052
|
+
txt_name?: string;
|
|
3053
|
+
txt_value?: string;
|
|
3054
|
+
}[];
|
|
3013
3055
|
}
|
|
3056
|
+
|
|
3014
3057
|
/**
|
|
3015
|
-
*
|
|
3016
|
-
*
|
|
3017
|
-
*
|
|
3058
|
+
* Leaderboard Types
|
|
3059
|
+
*
|
|
3060
|
+
* @module types/leaderboard
|
|
3018
3061
|
*/
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3062
|
+
type LeaderboardTimeframe = 'all_time' | 'monthly' | 'weekly' | 'daily';
|
|
3063
|
+
interface LeaderboardOptions {
|
|
3064
|
+
timeframe?: LeaderboardTimeframe;
|
|
3065
|
+
limit?: number;
|
|
3066
|
+
offset?: number;
|
|
3067
|
+
gameId?: string;
|
|
3025
3068
|
}
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3069
|
+
interface LeaderboardEntry {
|
|
3070
|
+
rank: number;
|
|
3071
|
+
userId: string;
|
|
3072
|
+
username: string;
|
|
3073
|
+
userImage?: string | null;
|
|
3074
|
+
score: number;
|
|
3075
|
+
achievedAt: Date;
|
|
3076
|
+
metadata?: Record<string, unknown>;
|
|
3077
|
+
gameId?: string;
|
|
3078
|
+
gameTitle?: string;
|
|
3079
|
+
gameSlug?: string;
|
|
3032
3080
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
live: boolean;
|
|
3039
|
-
/** Canonical quiz-level alignment used by mastery selection. */
|
|
3040
|
-
standard: AssessmentStandardRef | null;
|
|
3041
|
-
assessment: PlayableAssessment;
|
|
3042
|
-
/** Private comparison rules retained for hosted-equivalent local scoring when available. */
|
|
3043
|
-
scoring?: AssessmentScoringArtifactV1;
|
|
3044
|
-
/** Correct responses are retained by the host and are never returned to the game. */
|
|
3045
|
-
correctResponses: AssessmentResponses;
|
|
3046
|
-
/**
|
|
3047
|
-
* Host-only scored regions for point responses (select-point, position-object),
|
|
3048
|
-
* keyed by item then response identifier. A point response is correct when every
|
|
3049
|
-
* point lands in a distinct scored region.
|
|
3050
|
-
*/
|
|
3051
|
-
responseAreas?: Record<string, Record<string, AssessmentResponseArea[]>>;
|
|
3052
|
-
/** Host-only platform-routing sidecar for one immutable diagnostic bank revision. */
|
|
3053
|
-
diagnostic?: {
|
|
3054
|
-
diagnosticKey: string;
|
|
3055
|
-
/** Calculated from the canonical manifest when the fixture is built. */
|
|
3056
|
-
routingRevision: string;
|
|
3057
|
-
routingManifest: DiagnosticRoutingManifestV1;
|
|
3058
|
-
};
|
|
3059
|
-
/** Host-only standard index derived from this ordinary QTI test when used for review. */
|
|
3060
|
-
reviewBank?: {
|
|
3061
|
-
sourceContentRevision: string;
|
|
3062
|
-
bankRevision: string;
|
|
3063
|
-
items: {
|
|
3064
|
-
itemIdentifier: string;
|
|
3065
|
-
standards: AssessmentStandardRef[];
|
|
3066
|
-
}[];
|
|
3067
|
-
};
|
|
3081
|
+
interface UserRank {
|
|
3082
|
+
rank: number;
|
|
3083
|
+
totalPlayers: number;
|
|
3084
|
+
score: number;
|
|
3085
|
+
percentile: number;
|
|
3068
3086
|
}
|
|
3069
|
-
|
|
3070
|
-
|
|
3087
|
+
interface UserRankResponse {
|
|
3088
|
+
rank: number;
|
|
3089
|
+
score: number;
|
|
3090
|
+
userId: string;
|
|
3091
|
+
}
|
|
3092
|
+
interface UserScore {
|
|
3071
3093
|
id: string;
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3094
|
+
score: number;
|
|
3095
|
+
achievedAt: Date;
|
|
3096
|
+
metadata?: Record<string, unknown>;
|
|
3097
|
+
gameId: string;
|
|
3098
|
+
gameTitle: string;
|
|
3099
|
+
gameSlug: string;
|
|
3077
3100
|
}
|
|
3078
3101
|
/**
|
|
3079
|
-
*
|
|
3080
|
-
*
|
|
3081
|
-
* The version tracks this bundle's own shape, including the shape of the
|
|
3082
|
-
* assessments nested inside it — a bundle is rejected outright when either
|
|
3083
|
-
* differs, so a stale export is re-generated rather than half-read.
|
|
3102
|
+
* Leaderboard entry with required game context.
|
|
3103
|
+
* Used when fetching leaderboards for a specific game.
|
|
3084
3104
|
*/
|
|
3085
|
-
interface
|
|
3086
|
-
|
|
3105
|
+
interface GameLeaderboardEntry {
|
|
3106
|
+
rank: number;
|
|
3107
|
+
userId: string;
|
|
3108
|
+
username: string;
|
|
3109
|
+
userImage?: string | null;
|
|
3110
|
+
score: number;
|
|
3111
|
+
achievedAt: Date;
|
|
3112
|
+
metadata?: Record<string, unknown>;
|
|
3087
3113
|
gameId: string;
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
* Local dev matches on this (the platform gameId and the locally derived
|
|
3091
|
-
* id live in different namespaces, so they can never be compared).
|
|
3092
|
-
*/
|
|
3093
|
-
gameSlug?: string;
|
|
3094
|
-
exportedAt: string;
|
|
3095
|
-
catalogs: AssessmentRuntimeFixtureCatalog[];
|
|
3114
|
+
gameTitle: string;
|
|
3115
|
+
gameSlug: string;
|
|
3096
3116
|
}
|
|
3097
3117
|
|
|
3098
3118
|
/**
|
|
3099
|
-
*
|
|
3100
|
-
*
|
|
3101
|
-
*
|
|
3119
|
+
* Skill Status Types
|
|
3120
|
+
*
|
|
3121
|
+
* Contract for cross-game skill status queries. An answering game exports
|
|
3122
|
+
* `getSkillStatus` from `server/lib/skills.ts`; the platform calls its
|
|
3123
|
+
* reserved `/__playcademy/skills/status` route on behalf of another game.
|
|
3124
|
+
*
|
|
3125
|
+
* @module types/skills
|
|
3102
3126
|
*/
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3127
|
+
|
|
3128
|
+
/**
|
|
3129
|
+
* Current status of one skill for one student, as judged by the answering game.
|
|
3130
|
+
*
|
|
3131
|
+
* - `locked`: the student can't work on the skill yet in this game.
|
|
3132
|
+
* - `unlocked`: available to the student, but no progress yet.
|
|
3133
|
+
* - `in_progress`: started, not mastered.
|
|
3134
|
+
* - `mastered`: currently meets this game's mastery bar, including by placement.
|
|
3135
|
+
* - `unknown_skill`: the skill ID isn't recognized. Never report an unrecognized
|
|
3136
|
+
* skill as `locked`.
|
|
3137
|
+
*/
|
|
3138
|
+
type SkillStatus = (typeof SKILL_STATUSES)[number];
|
|
3139
|
+
/** How a `mastered` status was reached. */
|
|
3140
|
+
type SkillMasterySource = (typeof SKILL_MASTERY_SOURCES)[number];
|
|
3141
|
+
interface SkillStatusResult {
|
|
3142
|
+
/** Skill ID, lowercase. */
|
|
3143
|
+
skill: string;
|
|
3144
|
+
/** Current status. */
|
|
3145
|
+
status: SkillStatus;
|
|
3146
|
+
/** How mastery was reached. Only allowed when `status` is `mastered`. */
|
|
3147
|
+
source?: SkillMasterySource;
|
|
3148
|
+
/** ISO 8601 time of the evidence behind this status. */
|
|
3149
|
+
asOf?: string;
|
|
3150
|
+
}
|
|
3151
|
+
/** Response returned by an answering game's `getSkillStatus` handler. */
|
|
3152
|
+
interface SkillStatusResponse {
|
|
3153
|
+
/** Student identifier (Playcademy user ID). Must match the requested student. */
|
|
3154
|
+
studentId: string;
|
|
3155
|
+
/** One result per skill the handler was asked about. */
|
|
3156
|
+
results: SkillStatusResult[];
|
|
3157
|
+
}
|
|
3158
|
+
/** The game asking for skill status, as identified by the platform. */
|
|
3159
|
+
interface SkillStatusRequestingGame {
|
|
3160
|
+
id: string;
|
|
3161
|
+
slug: string;
|
|
3162
|
+
}
|
|
3163
|
+
/** Body of `POST /api/skills/status`, sent by the asking game's worker. */
|
|
3164
|
+
interface SkillStatusRequest {
|
|
3165
|
+
/** Slug of the game to ask. */
|
|
3166
|
+
game: string;
|
|
3167
|
+
/** Playcademy user ID of the student. */
|
|
3168
|
+
studentId: string;
|
|
3169
|
+
/** Skill IDs to ask about. Normalized (trimmed, lowercased) and de-duplicated. */
|
|
3170
|
+
skills: string[];
|
|
3117
3171
|
}
|
|
3172
|
+
/** Why the answering game couldn't answer. */
|
|
3173
|
+
type SkillStatusUnsupportedReason = 'not_implemented' | 'no_active_deployment' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
3174
|
+
/**
|
|
3175
|
+
* What the asking game gets back. When `supported` is false, fall back to
|
|
3176
|
+
* the game's own default (usually: treat the skills as not mastered).
|
|
3177
|
+
*/
|
|
3178
|
+
type SkillStatusLookupResult = {
|
|
3179
|
+
supported: true;
|
|
3180
|
+
results: SkillStatusResult[];
|
|
3181
|
+
} | {
|
|
3182
|
+
supported: false;
|
|
3183
|
+
reason: SkillStatusUnsupportedReason;
|
|
3184
|
+
};
|
|
3118
3185
|
|
|
3119
3186
|
/** A game token for a launch. `POST /api/embed/launches/:launchId/token`. */
|
|
3120
3187
|
interface EmbedGameToken {
|
|
@@ -6033,6 +6100,8 @@ interface EmbedTimebackRecording {
|
|
|
6033
6100
|
subject: TimebackSubject;
|
|
6034
6101
|
/** Course id hint, same semantics as `startActivity()`. */
|
|
6035
6102
|
courseId?: string;
|
|
6103
|
+
/** The student's enrollment in `courseId`; filled with it automatically. */
|
|
6104
|
+
enrollmentId?: string;
|
|
6036
6105
|
}
|
|
6037
6106
|
/**
|
|
6038
6107
|
* Everything a parent needs to resume an interrupted launch later. The
|
|
@@ -7137,6 +7206,8 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
7137
7206
|
get: (gameId: string) => Promise<GameTimebackIntegration[]>;
|
|
7138
7207
|
getRemovedIntegrations: (gameId: string) => Promise<RemovedGameTimebackIntegration[]>;
|
|
7139
7208
|
createIntegration: (gameId: string, request: CreateGameTimebackIntegrationRequest) => Promise<GameTimebackIntegration>;
|
|
7209
|
+
getHoleFillingCourses: (gameId: string) => Promise<GameTimebackIntegration[]>;
|
|
7210
|
+
createHoleFillingCourse: (gameId: string, request: CreateGameTimebackHoleFillingCourseRequest) => Promise<GameTimebackIntegration>;
|
|
7140
7211
|
updateIntegration: (gameId: string, courseId: string, request: UpdateGameTimebackIntegrationRequest) => Promise<GameTimebackIntegration>;
|
|
7141
7212
|
deactivateCourse: (gameId: string, courseId: string) => Promise<GameTimebackIntegration>;
|
|
7142
7213
|
reactivateCourse: (gameId: string, courseId: string) => Promise<GameTimebackIntegration>;
|