@playcademy/sdk 0.20.1-beta.2 → 0.20.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1202 -26
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +3195 -85
- package/dist/internal.js +1 -1
- package/dist/server/edge.d.ts +1298 -25
- package/dist/server/edge.js +1 -1
- package/dist/server.d.ts +1298 -25
- package/dist/server.js +1 -1
- package/dist/types.d.ts +1727 -29
- package/package.json +1 -1
package/dist/internal.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
import { SchemaInfo } from '@playcademy/cloudflare';
|
|
2
|
-
import { GamePermission, AUTH_PROVIDER_IDS } from '@playcademy/constants';
|
|
3
|
-
import { TimebackGrade, TimebackSubject, ELevel, HeartbeatRequest, EndActivityRequest, EndActivityScoreData, EndActivityResponse, TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/types/timeback';
|
|
4
|
-
export { AssessmentAttemptSnapshot, AssessmentAwardRecord, AssessmentFinalizeResult, AssessmentFlow, AssessmentItemGrading, AssessmentItemOutcome, AssessmentItemSubmission, AssessmentMasteryAward, AssessmentPreparationResult, AssessmentResponseUpdate, AssessmentResponseValue, AssessmentResponses, AssessmentReviewStandard, AssessmentSaveResult, AssessmentScore, AssessmentStandardRef, AssessmentSubmitResult, AssessmentTranscript, ConventionalAssessmentAttemptSnapshot, ConventionalAssessmentSubmitResult, DiagnosticAssessmentItemReceipt, DiagnosticAssessmentSubmitResult, DiagnosticRoutingSnapshot, DiagnosticRoutingTrackSnapshot, ELevel, FinalizeAssessmentInput, MasteryAssessmentSubmitResult, PlatformRoutedDiagnosticAttemptSnapshot, PlatformRoutedDiagnosticSelectionContext, PlayableAssessment, PlayableAssessmentAttemptSnapshot, PlayableAssessmentChoice, PlayableAssessmentGraphic, PlayableAssessmentHotspot, PlayableAssessmentInteraction, PlayableAssessmentItem, PlayableChoiceInteraction, PlayableContentNode, PlayableGapMatchChoice, PlayableGapMatchInteraction, PlayableInlineChoiceInteraction, PlayableMatchChoice, PlayableMatchInteraction, PlayableOrderInteraction, RecordAssessmentProgressInput, ResumeAssessmentInput, SaveAssessmentInput, SettledAssessmentAttemptSnapshot, StandardsReviewSelectionContext, StartAssessmentInput, StartAssessmentOptions, StartDiagnosticAssessmentInput, SubmitAssessmentInput, SubmitAssessmentItemInput, SubmitAssessmentItemResult, SubmitDiagnosticAssessmentItemInput, SubmitDiagnosticAssessmentItemResult } from '@playcademy/types/timeback';
|
|
5
|
-
import * as _playcademy_types from '@playcademy/types';
|
|
6
|
-
import { GameManifest, LocalDayContext } from '@playcademy/types';
|
|
7
|
-
export { AuthenticatedUser, DeploySource, DeveloperStatusEnumType, DeveloperStatusResponse, DeveloperStatusValue, GameCourseMetrics, GameLeaderboardEntry, GameManifest, GameMetricComparisonKind, GameMetricComparisonMetric, GameMetricComparisonRow, GameMetricComparisonRowStatus, GameMetricsProxyResponse, GameMetricsResponse, GameMetricsUnsupportedReason, GamePlatform, GameReleaseResponse, GameRunMetrics, GameRunMetricsComparison, GameRunMetricsComparisonStatus, GameRunMetricsComparisonSummary, GameTimebackIntegration, GameType, GameUser, LeaderboardEntry, LeaderboardOptions, LeaderboardTimeframe, LocalDayContext, LocalDaySource, ManifestV1, ManifestV2, ManifestVersions, PopulateStudentResponse, Release, ReleasesResponse, SkillMasterySource, SkillStatus, SkillStatusLookupResult, SkillStatusRequest, SkillStatusRequestingGame, SkillStatusResponse, SkillStatusResult, SkillStatusUnsupportedReason, UserEnrollment, UserInfo, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData } from '@playcademy/types';
|
|
8
1
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
9
|
-
import { DomainValidationRecords } from '@playcademy/types/game';
|
|
10
2
|
import { z } from 'zod';
|
|
11
|
-
import { TimebackUserRole, UserEnrollment, UserOrganization, UserInfo } from '@playcademy/types/user';
|
|
12
3
|
|
|
13
4
|
/** Permitted HTTP verbs */
|
|
14
5
|
type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
@@ -17,6 +8,3108 @@ interface RetryPolicy {
|
|
|
17
8
|
retryDelaysMs?: readonly number[];
|
|
18
9
|
}
|
|
19
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Authentication Constants
|
|
13
|
+
*
|
|
14
|
+
* Constants related to authentication providers.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Auth Provider Constants
|
|
18
|
+
*/
|
|
19
|
+
declare const AUTH_PROVIDER_IDS: {
|
|
20
|
+
readonly TIMEBACK: 'timeback';
|
|
21
|
+
readonly TIMEBACK_LTI: 'timeback-lti';
|
|
22
|
+
readonly PLAYCADEMY: 'playcademy';
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Platform user roles.
|
|
26
|
+
*/
|
|
27
|
+
declare const USER_ROLES: {
|
|
28
|
+
readonly ADMIN: 'admin';
|
|
29
|
+
readonly PLAYER: 'player';
|
|
30
|
+
readonly DEVELOPER: 'developer';
|
|
31
|
+
readonly TEACHER: 'teacher';
|
|
32
|
+
};
|
|
33
|
+
type UserRole = (typeof USER_ROLES)[keyof typeof USER_ROLES];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Browser permissions a game may declare and the hub may delegate to its
|
|
37
|
+
* iframe. Opt-in per game because a delegated grant keys to the hub's
|
|
38
|
+
* top-level origin: one player grant would otherwise reach every embedded
|
|
39
|
+
* game. Baseline features (fullscreen, autoplay, gamepad) are delegated to
|
|
40
|
+
* all games and are not part of this set.
|
|
41
|
+
*/
|
|
42
|
+
declare const GAME_PERMISSIONS: readonly ['microphone', 'camera'];
|
|
43
|
+
type GamePermission = (typeof GAME_PERMISSIONS)[number];
|
|
44
|
+
|
|
45
|
+
/** Every status an answering game may report for a skill. */
|
|
46
|
+
declare const SKILL_STATUSES: readonly ['locked', 'unlocked', 'in_progress', 'mastered', 'unknown_skill'];
|
|
47
|
+
/** Every way a `mastered` status may have been reached. */
|
|
48
|
+
declare const SKILL_MASTERY_SOURCES: readonly ['assessment', 'placement', 'test_out', 'manual'];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Valid grade levels per AE OneRoster GradeEnum.
|
|
52
|
+
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
53
|
+
*/
|
|
54
|
+
declare const TIMEBACK_GRADES: readonly [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
55
|
+
/**
|
|
56
|
+
* The TimeBack subject vocabulary, carried on courses, activity data, and
|
|
57
|
+
* Caliper events alike. 'None' is canonical: TimeBack accepts it as a course
|
|
58
|
+
* subject and uses it as the fallback for unknown or missing subjects.
|
|
59
|
+
*/
|
|
60
|
+
declare const TIMEBACK_SUBJECTS: readonly ['Reading', 'Language', 'Vocabulary', 'Social Studies', 'Writing', 'Science', 'FastMath', 'Math', 'None'];
|
|
61
|
+
/**
|
|
62
|
+
* Playcademy's course-specific meaning for reusable QTI test content.
|
|
63
|
+
* Result writers translate this to OneRoster assessment-result
|
|
64
|
+
* `metadata.testType`; it intentionally does not become metadata on the QTI
|
|
65
|
+
* test itself.
|
|
66
|
+
*/
|
|
67
|
+
declare const ASSESSMENT_PURPOSES: readonly ['end_of_course', 'diagnostic', 'review', 'mastery'];
|
|
68
|
+
/**
|
|
69
|
+
* Schema version of the playable assessment payload handed to a game.
|
|
70
|
+
*
|
|
71
|
+
* Distinct from an assessment's content revision: this identifies the shape of
|
|
72
|
+
* the contract, not the content inside it. Bumped when the payload gains a
|
|
73
|
+
* field or changes the meaning of one — never for content edits.
|
|
74
|
+
*
|
|
75
|
+
* Intended for diagnostics and for telling "this host does not support the
|
|
76
|
+
* field" apart from "this item does not use it". Games should branch on whether
|
|
77
|
+
* a field is present, not on this number: the content vocabularies are open by
|
|
78
|
+
* design, so feature detection keeps working across versions where a comparison
|
|
79
|
+
* does not.
|
|
80
|
+
*/
|
|
81
|
+
declare const PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION: 2;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* User Types
|
|
85
|
+
*
|
|
86
|
+
* Enums, DTOs and API response types. Database row types are in @playcademy/data/types.
|
|
87
|
+
*
|
|
88
|
+
* @module types/user
|
|
89
|
+
*/
|
|
90
|
+
|
|
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
|
+
interface UserTimebackData extends TimebackStudentProfile {
|
|
115
|
+
id: string;
|
|
116
|
+
enrollments: UserEnrollment[];
|
|
117
|
+
}
|
|
118
|
+
type LocalDaySource = 'browser_last_seen' | 'platform_default';
|
|
119
|
+
interface LocalDayContext {
|
|
120
|
+
timeZone: string;
|
|
121
|
+
source: LocalDaySource;
|
|
122
|
+
/** ISO 8601 timestamp for the last persisted timezone observation. */
|
|
123
|
+
observedAt: string | null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* OpenID Connect UserInfo claims (NOT a database row).
|
|
127
|
+
*/
|
|
128
|
+
interface UserInfo {
|
|
129
|
+
sub: string;
|
|
130
|
+
email: string;
|
|
131
|
+
name: string | null;
|
|
132
|
+
email_verified?: boolean;
|
|
133
|
+
given_name?: string;
|
|
134
|
+
family_name?: string;
|
|
135
|
+
issuer?: string;
|
|
136
|
+
lti_roles?: unknown;
|
|
137
|
+
lti_context?: unknown;
|
|
138
|
+
lti_resource_link?: unknown;
|
|
139
|
+
timeback_id?: string;
|
|
140
|
+
}
|
|
141
|
+
interface DeveloperStatusResponse {
|
|
142
|
+
status: DeveloperStatusEnumType;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Authenticated user for API responses.
|
|
146
|
+
* Differs from UserRow: omits timebackId, adds hasTimebackAccount and timeback.
|
|
147
|
+
*/
|
|
148
|
+
interface AuthenticatedUser {
|
|
149
|
+
id: string;
|
|
150
|
+
email: string;
|
|
151
|
+
emailVerified: boolean;
|
|
152
|
+
name: string | null;
|
|
153
|
+
image: string | null;
|
|
154
|
+
username: string | null;
|
|
155
|
+
role: UserRoleEnumType;
|
|
156
|
+
developerStatus: DeveloperStatusEnumType;
|
|
157
|
+
createdAt: Date;
|
|
158
|
+
updatedAt: Date;
|
|
159
|
+
hasTimebackAccount: boolean;
|
|
160
|
+
localDay: LocalDayContext;
|
|
161
|
+
timeback?: UserTimebackData;
|
|
162
|
+
}
|
|
163
|
+
interface GameUser {
|
|
164
|
+
id: string;
|
|
165
|
+
name: string | null;
|
|
166
|
+
role: UserRoleEnumType;
|
|
167
|
+
username: string | null;
|
|
168
|
+
email: string | null;
|
|
169
|
+
localDay: LocalDayContext;
|
|
170
|
+
timeback?: UserTimebackData;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Game Types
|
|
175
|
+
*
|
|
176
|
+
* Literal types and API DTOs. Database row types are in @playcademy/data/types.
|
|
177
|
+
*
|
|
178
|
+
* @module types/game
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
type GameType = 'hosted' | 'external';
|
|
182
|
+
type GamePlatform = 'web' | 'godot' | (string & {});
|
|
183
|
+
/**
|
|
184
|
+
* Game manifest file format (manifest.json).
|
|
185
|
+
* Note: createdAt is a string here because it's parsed from JSON file.
|
|
186
|
+
*/
|
|
187
|
+
interface ManifestV1 {
|
|
188
|
+
version: '1';
|
|
189
|
+
platform: string;
|
|
190
|
+
createdAt: string;
|
|
191
|
+
}
|
|
192
|
+
interface ManifestVersions {
|
|
193
|
+
vitePlugin?: string;
|
|
194
|
+
sdk?: string;
|
|
195
|
+
cli?: string;
|
|
196
|
+
vite?: string;
|
|
197
|
+
godotSdk?: string;
|
|
198
|
+
godot?: string;
|
|
199
|
+
}
|
|
200
|
+
interface ManifestV2 {
|
|
201
|
+
version: '2';
|
|
202
|
+
platform: string;
|
|
203
|
+
createdAt: string;
|
|
204
|
+
gameVersion?: string;
|
|
205
|
+
versions: ManifestVersions;
|
|
206
|
+
}
|
|
207
|
+
type GameManifest = ManifestV1 | ManifestV2;
|
|
208
|
+
/** One migration in a migrate-mode deploy payload */
|
|
209
|
+
interface DeployMigration {
|
|
210
|
+
/** Journal tag, e.g. '0042_brave_hulk' */
|
|
211
|
+
tag: string;
|
|
212
|
+
/** Pre-split SQL statements (the CLI splits on drizzle statement-breakpoints) */
|
|
213
|
+
statements: string[];
|
|
214
|
+
/** sha-256 of the normalized migration SQL (sha256-v1) */
|
|
215
|
+
checksum: string;
|
|
216
|
+
}
|
|
217
|
+
/** Push-mode database payload: diff-generated SQL guarded by a baseline CAS */
|
|
218
|
+
interface DeployDatabasePush {
|
|
219
|
+
mode: 'push';
|
|
220
|
+
/** Diff-generated DDL to execute (semicolon-separated statements) */
|
|
221
|
+
sql: string;
|
|
222
|
+
/** Stored schemaHash the diff was computed against; null only for a first deploy */
|
|
223
|
+
baselineHash: string | null;
|
|
224
|
+
/** Drizzle snapshot JSON this deploy moves the schema to */
|
|
225
|
+
nextSnapshot: unknown;
|
|
226
|
+
/** Hash of nextSnapshot, stored as the new push baseline on success */
|
|
227
|
+
nextHash: string;
|
|
228
|
+
/** Explicit consent for destructive statements (DROP TABLE, dropped columns) */
|
|
229
|
+
acceptDataLoss?: boolean;
|
|
230
|
+
}
|
|
231
|
+
/** Migrate-mode database payload: the client's complete migration journal */
|
|
232
|
+
interface DeployDatabaseMigrate {
|
|
233
|
+
mode: 'migrate';
|
|
234
|
+
/** The full journal in order; the server applies whatever the ledger lacks */
|
|
235
|
+
migrations: DeployMigration[];
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Mode-aware database payload (PLA-217 D5). Replaces the legacy `schema`
|
|
239
|
+
* field for adopted games; the server verifies state before executing.
|
|
240
|
+
*/
|
|
241
|
+
type DeployDatabasePayload = DeployDatabasePush | DeployDatabaseMigrate;
|
|
242
|
+
/** Journal entry in a baseline adoption block */
|
|
243
|
+
interface DeployBaselineJournalEntry {
|
|
244
|
+
tag: string;
|
|
245
|
+
checksum: string;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Baseline adoption block (PLA-217 D7 seed-on-deploy): the client's claimed
|
|
249
|
+
* starting state for a game that predates server-side deployment state.
|
|
250
|
+
* Consumed by the deploy itself — an unadopted game gets its claimed ledger
|
|
251
|
+
* rows, snapshot/hash, and live fingerprint recorded before the database
|
|
252
|
+
* step runs; adopted games skip the block.
|
|
253
|
+
*/
|
|
254
|
+
interface DeployBaseline {
|
|
255
|
+
lastAppliedMigrationTag?: string;
|
|
256
|
+
journal?: DeployBaselineJournalEntry[];
|
|
257
|
+
/**
|
|
258
|
+
* Per-migration snapshot deltas for content validation (D7): the
|
|
259
|
+
* seed claim is judged against the live database exactly like a
|
|
260
|
+
* manual `db baseline` claim — the side door uses the front door's
|
|
261
|
+
* checkpoint.
|
|
262
|
+
*/
|
|
263
|
+
evidence?: BaselineMigrationEvidence[];
|
|
264
|
+
schemaSnapshot?: unknown;
|
|
265
|
+
schemaHash?: string;
|
|
266
|
+
integrationsHash?: string;
|
|
267
|
+
buildHash?: string;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Git provenance of the working tree a deploy was run from (PLA-294).
|
|
271
|
+
*
|
|
272
|
+
* Captured by the CLI at deploy time and persisted verbatim on the deploy
|
|
273
|
+
* job, so "which commit is running" is answerable from the platform record.
|
|
274
|
+
* Every field is nullable: a tree with no git, no remote, or a detached HEAD
|
|
275
|
+
* is recorded honestly rather than refused. Recorded per deploy, never
|
|
276
|
+
* pinned to the game — a fork deployed under the same slug shows the fork.
|
|
277
|
+
*/
|
|
278
|
+
interface DeploySource {
|
|
279
|
+
/** Normalized `https://github.com/<org>/<repo>`; null when no remote or unparseable */
|
|
280
|
+
repository: string | null;
|
|
281
|
+
/** Full 40-char SHA of HEAD; null when not in a git tree */
|
|
282
|
+
commit: string | null;
|
|
283
|
+
/** Current branch name; null on detached HEAD outside CI */
|
|
284
|
+
branch: string | null;
|
|
285
|
+
/** True when the tree had uncommitted changes — the SHA does not describe what shipped */
|
|
286
|
+
dirty: boolean;
|
|
287
|
+
/**
|
|
288
|
+
* True when the uploaded build artifact lives outside the git tree the
|
|
289
|
+
* other fields describe (`--build ~/Downloads/game.zip`). The commit is
|
|
290
|
+
* still the deployer's tree, but it may not be what produced the artifact.
|
|
291
|
+
*/
|
|
292
|
+
externalBuild: boolean;
|
|
293
|
+
}
|
|
294
|
+
type DeployJobStatus = 'pending' | 'running' | 'succeeded' | 'failed';
|
|
295
|
+
/** Active game worker state served by the deployment-state endpoint */
|
|
296
|
+
interface DeploymentStateGame {
|
|
297
|
+
/** Backend bundle hash from the active deployment row */
|
|
298
|
+
codeHash: string | null;
|
|
299
|
+
/** Frontend zip hash from game_deployment_state (null when unseeded) */
|
|
300
|
+
buildHash: string | null;
|
|
301
|
+
url: string;
|
|
302
|
+
deployedAt: string;
|
|
303
|
+
}
|
|
304
|
+
/** Active dashboard worker state served by the deployment-state endpoint */
|
|
305
|
+
interface DeploymentStateDashboard {
|
|
306
|
+
url: string;
|
|
307
|
+
deployedAt: string;
|
|
308
|
+
}
|
|
309
|
+
/** Details of the last failed migration, from the failed deploy job */
|
|
310
|
+
interface DeploymentStateDatabaseFailure {
|
|
311
|
+
tag: string;
|
|
312
|
+
error: string;
|
|
313
|
+
/**
|
|
314
|
+
* Set only when the failing statement is known exactly (the size
|
|
315
|
+
* preflight names the statement it measured). Execution failures are
|
|
316
|
+
* null: D1's byte offset has an unverified reference frame for the
|
|
317
|
+
* batch array form, so it is never mapped to an index.
|
|
318
|
+
*/
|
|
319
|
+
statementIndex: number | null;
|
|
320
|
+
at: string;
|
|
321
|
+
}
|
|
322
|
+
/** A ledger entry from the game database's applied-migrations ledger */
|
|
323
|
+
interface DeploymentStateAppliedMigration {
|
|
324
|
+
tag: string;
|
|
325
|
+
/** Checksum recorded at apply time (normalized SQL, see checksumAlgo) */
|
|
326
|
+
checksum: string;
|
|
327
|
+
/** Hash algorithm the checksum was recorded under (e.g. 'sha256-v1') */
|
|
328
|
+
checksumAlgo: string;
|
|
329
|
+
}
|
|
330
|
+
/** Database schema state served by the deployment-state endpoint */
|
|
331
|
+
interface DeploymentStateDatabase {
|
|
332
|
+
/** Applied migrations from the live in-D1 ledger (null when the game has no database) */
|
|
333
|
+
appliedMigrations: DeploymentStateAppliedMigration[] | null;
|
|
334
|
+
lastFailure: DeploymentStateDatabaseFailure | null;
|
|
335
|
+
/** Hash of the drizzle snapshot (push-mode baseline) */
|
|
336
|
+
schemaHash: string | null;
|
|
337
|
+
/** Hash of the normalized live sqlite_master */
|
|
338
|
+
schemaFingerprint: string | null;
|
|
339
|
+
/**
|
|
340
|
+
* Stored drizzle snapshot (push-mode diffing only). Present only when
|
|
341
|
+
* the request asked for it with `?include=schemaSnapshot` (D4).
|
|
342
|
+
*/
|
|
343
|
+
schemaSnapshot?: unknown;
|
|
344
|
+
}
|
|
345
|
+
/** Secret key names partitioned by ownership — names only, never hashes */
|
|
346
|
+
interface DeploymentStateSecrets {
|
|
347
|
+
/** Keys recorded in the server-side secrets manifest */
|
|
348
|
+
managedKeys: string[];
|
|
349
|
+
/**
|
|
350
|
+
* Remote keys not in the manifest (set by SST, wrangler, or hand).
|
|
351
|
+
* Null when the server has no Cloudflare provider to list remote keys.
|
|
352
|
+
*/
|
|
353
|
+
unmanagedKeys: string[] | null;
|
|
354
|
+
}
|
|
355
|
+
interface DeploymentStateLastDeploy {
|
|
356
|
+
at: string;
|
|
357
|
+
/** Who deployed: their email, or the raw user id when the account no longer resolves */
|
|
358
|
+
by: string;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Server-side deployment state for a game.
|
|
362
|
+
*
|
|
363
|
+
* Everything CLI deploy prep diffs against, fetched from
|
|
364
|
+
* GET /api/games/:slug/deployment-state so deploys never depend on
|
|
365
|
+
* local state for correctness.
|
|
366
|
+
*/
|
|
367
|
+
interface DeploymentStateResponse {
|
|
368
|
+
gameId: string;
|
|
369
|
+
/** True iff a game_deployment_state row exists for the game */
|
|
370
|
+
seeded: boolean;
|
|
371
|
+
game: DeploymentStateGame | null;
|
|
372
|
+
dashboard: DeploymentStateDashboard | null;
|
|
373
|
+
database: DeploymentStateDatabase;
|
|
374
|
+
secrets: DeploymentStateSecrets;
|
|
375
|
+
/** Hash of the declared integrations config (null when unseeded) */
|
|
376
|
+
integrationsHash: string | null;
|
|
377
|
+
/** Last deployed compatibility date (null when unseeded) */
|
|
378
|
+
compatibilityDate: string | null;
|
|
379
|
+
lastDeploy: DeploymentStateLastDeploy | null;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Per-key verdict groups from the server-side secrets diff (D6).
|
|
383
|
+
*
|
|
384
|
+
* Key names only — HMAC digests never leave the server. `remoteOnlyManaged`
|
|
385
|
+
* keys are the only legal prune candidates; `remoteOnlyUnmanaged` keys were
|
|
386
|
+
* set outside the platform and are untouchable.
|
|
387
|
+
*/
|
|
388
|
+
interface SecretsDiffResponse {
|
|
389
|
+
/** Local keys the manifest doesn't know yet */
|
|
390
|
+
added: string[];
|
|
391
|
+
/** Keys whose local value's HMAC differs from the manifest's */
|
|
392
|
+
changed: string[];
|
|
393
|
+
/** Keys whose local value's HMAC matches the manifest's */
|
|
394
|
+
unchanged: string[];
|
|
395
|
+
/** Manifest keys absent locally (prunable) */
|
|
396
|
+
remoteOnlyManaged: string[];
|
|
397
|
+
/** Worker keys absent from both the manifest and the local set */
|
|
398
|
+
remoteOnlyUnmanaged: string[];
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Manual baseline adoption request (PLA-217 D7): the fresh-machine path for
|
|
402
|
+
* a game that predates server-side deployment state. Carries the claimed
|
|
403
|
+
* migrate-mode history (tag + journal), the push-mode snapshot, or both.
|
|
404
|
+
*/
|
|
405
|
+
interface DeploymentStateBaselineRequest {
|
|
406
|
+
lastAppliedMigrationTag?: string;
|
|
407
|
+
journal?: DeployBaselineJournalEntry[];
|
|
408
|
+
schemaSnapshot?: unknown;
|
|
409
|
+
schemaHash?: string;
|
|
410
|
+
/**
|
|
411
|
+
* Per-migration schema-object evidence (creates AND drops) derived
|
|
412
|
+
* from drizzle's snapshot files. The server replays it up to the
|
|
413
|
+
* claimed tag into the expected schema and validates that against the
|
|
414
|
+
* live database (D7): surviving claimed objects must exist,
|
|
415
|
+
* beyond-claim objects must be absent unless claimed history already
|
|
416
|
+
* explains them. Absent evidence downgrades every migration to
|
|
417
|
+
* no-signal.
|
|
418
|
+
*/
|
|
419
|
+
evidence?: BaselineMigrationEvidence[];
|
|
420
|
+
/**
|
|
421
|
+
* Waive refusals for no-signal migrations only (content contradictions
|
|
422
|
+
* are never waivable). Recorded server-side for audit.
|
|
423
|
+
*/
|
|
424
|
+
allowUnverified?: boolean;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Schema objects one journal migration introduced and removed (snapshot
|
|
428
|
+
* delta). Drops are as load-bearing as creates: an object created by one
|
|
429
|
+
* migration and dropped by a later one must not be demanded of the live
|
|
430
|
+
* database, and its live presence beyond the claim must not convict a
|
|
431
|
+
* migration whose object is already explained by claimed history.
|
|
432
|
+
*/
|
|
433
|
+
interface BaselineMigrationEvidence {
|
|
434
|
+
tag: string;
|
|
435
|
+
/** Journal generation timestamp (drizzle `when`), ISO 8601 */
|
|
436
|
+
generatedAt: string;
|
|
437
|
+
createsTables: {
|
|
438
|
+
name: string;
|
|
439
|
+
columns: string[];
|
|
440
|
+
}[];
|
|
441
|
+
addsColumns: {
|
|
442
|
+
table: string;
|
|
443
|
+
column: string;
|
|
444
|
+
}[];
|
|
445
|
+
createsIndexes: string[];
|
|
446
|
+
createsViews: string[];
|
|
447
|
+
dropsTables: string[];
|
|
448
|
+
dropsColumns: {
|
|
449
|
+
table: string;
|
|
450
|
+
column: string;
|
|
451
|
+
}[];
|
|
452
|
+
dropsIndexes: string[];
|
|
453
|
+
dropsViews: string[];
|
|
454
|
+
}
|
|
455
|
+
/** Outcome of the `db realign <tag>` checksum pardon */
|
|
456
|
+
interface MigrationRealignResponse {
|
|
457
|
+
tag: string;
|
|
458
|
+
/** The re-recorded checksum */
|
|
459
|
+
checksum: string;
|
|
460
|
+
/** Algorithm stamped alongside it (e.g. 'sha256-v1') */
|
|
461
|
+
checksumAlgo: string;
|
|
462
|
+
}
|
|
463
|
+
/** How a ledger escape-hatch resolution lands: record the row, or remove it */
|
|
464
|
+
type MigrationResolution = 'applied' | 'rolled-back';
|
|
465
|
+
/** Outcome of the `db resolve <tag>` ledger escape hatch */
|
|
466
|
+
interface MigrationResolveResponse {
|
|
467
|
+
tag: string;
|
|
468
|
+
resolution: MigrationResolution;
|
|
469
|
+
/**
|
|
470
|
+
* Live schema fingerprint captured by the resolve: the developer's
|
|
471
|
+
* attestation that the current database state is intended, so the next
|
|
472
|
+
* deploy verifies against it instead of the pre-repair record.
|
|
473
|
+
*/
|
|
474
|
+
schemaFingerprint: string;
|
|
475
|
+
/**
|
|
476
|
+
* Whether the fingerprint was persisted to the game's deployment-state
|
|
477
|
+
* row. False when the game has no such row (not yet adopted): the
|
|
478
|
+
* ledger repair still succeeded, but nothing was recorded.
|
|
479
|
+
*/
|
|
480
|
+
fingerprintRecorded: boolean;
|
|
481
|
+
}
|
|
482
|
+
/** One deploy job in a game's execution history (PLA-217 D10) */
|
|
483
|
+
interface GameDeployHistoryDeploy {
|
|
484
|
+
kind: 'deploy';
|
|
485
|
+
status: DeployJobStatus;
|
|
486
|
+
/** When the deploy landed (creation time while pending/running), ISO 8601 */
|
|
487
|
+
at: string;
|
|
488
|
+
/** When the job finished (null while pending/running) */
|
|
489
|
+
completedAt: string | null;
|
|
490
|
+
/** Who deployed: their email, or DELETED_ACCOUNT_LABEL when the account is gone */
|
|
491
|
+
by: string;
|
|
492
|
+
/** Client idempotency key (null for legacy clients) */
|
|
493
|
+
deployId: string | null;
|
|
494
|
+
error: string | null;
|
|
495
|
+
}
|
|
496
|
+
/** A Time Travel restore in a game's execution history */
|
|
497
|
+
interface GameDeployHistoryRestore {
|
|
498
|
+
kind: 'restore';
|
|
499
|
+
/** When the restore ran, ISO 8601 */
|
|
500
|
+
at: string;
|
|
501
|
+
/** Who restored: their email, or DELETED_ACCOUNT_LABEL when the account is gone */
|
|
502
|
+
by: string;
|
|
503
|
+
/** The moment the database was rewound to, ISO 8601 */
|
|
504
|
+
restoredTo: string;
|
|
505
|
+
}
|
|
506
|
+
/** A client-gate refusal in a game's execution history */
|
|
507
|
+
interface GameDeployHistoryBlocked {
|
|
508
|
+
kind: 'blocked';
|
|
509
|
+
/** When the gate refused, ISO 8601 */
|
|
510
|
+
at: string;
|
|
511
|
+
/** Whose deploy was refused: their email, or DELETED_ACCOUNT_LABEL */
|
|
512
|
+
by: string;
|
|
513
|
+
/** Stable `blocked:*` code the gate refused with */
|
|
514
|
+
code: string;
|
|
515
|
+
/** The gate's plain-text refusal reason */
|
|
516
|
+
reason: string;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* One entry in a game's execution history: deploy jobs interleaved with
|
|
520
|
+
* the platform-visible events the job system never sees (restores,
|
|
521
|
+
* client-gate refusals), newest first.
|
|
522
|
+
*/
|
|
523
|
+
type GameDeployHistoryEntry = GameDeployHistoryDeploy | GameDeployHistoryRestore | GameDeployHistoryBlocked;
|
|
524
|
+
/** Deploy history served by GET /api/games/:slug/deployments */
|
|
525
|
+
interface GameDeployHistoryResponse {
|
|
526
|
+
deploys: GameDeployHistoryEntry[];
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* One successful deploy of a game's worker, as the open releases surface
|
|
530
|
+
* describes it: what is running and which commit produced it.
|
|
531
|
+
*
|
|
532
|
+
* This shape is parsed by external automation. Every field is present on
|
|
533
|
+
* every entry — nullable, never absent — and nothing from the deploy job
|
|
534
|
+
* beyond its git provenance and completion time is exposed. No deployer
|
|
535
|
+
* identity, no schema state, no secrets manifest.
|
|
536
|
+
*/
|
|
537
|
+
interface Release {
|
|
538
|
+
slug: string;
|
|
539
|
+
/** Where the game is served; null when the platform has no URL for it */
|
|
540
|
+
url: string | null;
|
|
541
|
+
/**
|
|
542
|
+
* Git provenance the deploying CLI captured. Null for deploys that
|
|
543
|
+
* predate provenance capture, so the fleet list is complete on day one
|
|
544
|
+
* and a consumer can tell "unknown" from "not in git".
|
|
545
|
+
*/
|
|
546
|
+
source: DeploySource | null;
|
|
547
|
+
/** When the deploy succeeded, ISO 8601 */
|
|
548
|
+
deployedAt: string;
|
|
549
|
+
}
|
|
550
|
+
/** GET /api/releases — the current release of every game */
|
|
551
|
+
interface ReleasesResponse {
|
|
552
|
+
releases: Release[];
|
|
553
|
+
}
|
|
554
|
+
/** GET /api/releases/:slug — one game's current release and, on request, its recent history */
|
|
555
|
+
interface GameReleaseResponse {
|
|
556
|
+
/** The current release; null when the game has never deployed successfully */
|
|
557
|
+
current: Release | null;
|
|
558
|
+
/**
|
|
559
|
+
* The last N successful deploys, newest first, current included. Only
|
|
560
|
+
* present when `?history=N` was requested.
|
|
561
|
+
*/
|
|
562
|
+
history?: Release[];
|
|
563
|
+
}
|
|
564
|
+
/** Request body for POST /api/games/:slug/deployments/blocked */
|
|
565
|
+
interface DeployBlockedReport {
|
|
566
|
+
/** Stable `blocked:*` code the gate refused with */
|
|
567
|
+
code: string;
|
|
568
|
+
/** The gate's plain-text refusal reason */
|
|
569
|
+
reason: string;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* A restorable pre-deploy database state (D1 Time Travel bookmark).
|
|
573
|
+
*
|
|
574
|
+
* Each schema-bearing deploy captures a bookmark of the database as it was
|
|
575
|
+
* the moment before that deploy's schema changes ran. Only bookmarks
|
|
576
|
+
* belonging to the CURRENT database and within Time Travel's retention
|
|
577
|
+
* window are served — a `db reset` creates a new database (bookmarks do
|
|
578
|
+
* not cross that boundary), and Cloudflare expires bookmarks after the
|
|
579
|
+
* account plan's retention period.
|
|
580
|
+
*/
|
|
581
|
+
interface GameRestorePoint {
|
|
582
|
+
/** Deployment row id — pass to the restore endpoint (or a unique prefix to the CLI) */
|
|
583
|
+
id: string;
|
|
584
|
+
/**
|
|
585
|
+
* When the bookmark was captured (the exact state a restore rewinds
|
|
586
|
+
* to), ISO 8601. Rows from before capture times were recorded fall
|
|
587
|
+
* back to the deploy completion time.
|
|
588
|
+
*/
|
|
589
|
+
capturedAt: string;
|
|
590
|
+
/** Whether the capturing deployment is the currently active one */
|
|
591
|
+
active: boolean;
|
|
592
|
+
}
|
|
593
|
+
/** Restore points served by GET /api/games/:slug/database/restore-points */
|
|
594
|
+
interface GameRestorePointsResponse {
|
|
595
|
+
restorePoints: GameRestorePoint[];
|
|
596
|
+
/**
|
|
597
|
+
* True for push-adopted games, whose recorded snapshot cannot rewind
|
|
598
|
+
* with the data: restore is categorically refused, so the list is
|
|
599
|
+
* served empty rather than offering points that can never restore.
|
|
600
|
+
*/
|
|
601
|
+
restoreUnsupported: boolean;
|
|
602
|
+
}
|
|
603
|
+
/** Outcome of a Time Travel restore (PLA-217 D10) */
|
|
604
|
+
interface DatabaseRestoreResponse {
|
|
605
|
+
restorePointId: string;
|
|
606
|
+
/** The moment the database was rewound to (the bookmark's capture time), ISO 8601 */
|
|
607
|
+
restoredTo: string;
|
|
608
|
+
/**
|
|
609
|
+
* Live schema fingerprint captured after the restore: the developer's
|
|
610
|
+
* audited attestation that the rewound state is intended, so the next
|
|
611
|
+
* deploy verifies against it.
|
|
612
|
+
*/
|
|
613
|
+
schemaFingerprint: string;
|
|
614
|
+
/** False when the game has no deployment-state row to re-attest */
|
|
615
|
+
fingerprintRecorded: boolean;
|
|
616
|
+
/**
|
|
617
|
+
* Bookmark of the database as it was just before this restore — the
|
|
618
|
+
* undo handle. Null only if Cloudflare omits it from the response.
|
|
619
|
+
*/
|
|
620
|
+
previousBookmark: string | null;
|
|
621
|
+
}
|
|
622
|
+
/** Push-mode reset payload: the full schema that rebuilds the fresh database */
|
|
623
|
+
interface DatabaseResetPush {
|
|
624
|
+
mode: 'push';
|
|
625
|
+
/** Full schema DDL (semicolon-separated statements) */
|
|
626
|
+
sql: string;
|
|
627
|
+
/** Drizzle snapshot JSON, stored as the new push baseline */
|
|
628
|
+
nextSnapshot: unknown;
|
|
629
|
+
/** Hash of nextSnapshot, stored as the new schemaHash */
|
|
630
|
+
nextHash: string;
|
|
631
|
+
}
|
|
632
|
+
/** Migrate-mode reset payload: the complete journal to replay on the fresh database */
|
|
633
|
+
interface DatabaseResetMigrate {
|
|
634
|
+
mode: 'migrate';
|
|
635
|
+
/** The full journal in order; replayed one-by-one, rebuilding the ledger */
|
|
636
|
+
migrations: DeployMigration[];
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Mode-aware reset payload (PLA-217 D2). Reset recreates the database, so
|
|
640
|
+
* the ledger dies with it and there is no baseline to compare against —
|
|
641
|
+
* adopted games must send how to rebuild.
|
|
642
|
+
*/
|
|
643
|
+
type DatabaseResetDatabasePayload = DatabaseResetPush | DatabaseResetMigrate;
|
|
644
|
+
interface DatabaseResetRequest {
|
|
645
|
+
/**
|
|
646
|
+
* Legacy schema info, accepted only while the game's schema state is
|
|
647
|
+
* unadopted; adopted games must send `database` instead.
|
|
648
|
+
*/
|
|
649
|
+
schema?: {
|
|
650
|
+
/** SQL schema statements (DDL: CREATE, ALTER, etc.) */
|
|
651
|
+
sql: string;
|
|
652
|
+
/** Hash of the schema for change detection */
|
|
653
|
+
hash: string;
|
|
654
|
+
};
|
|
655
|
+
/** Mode-aware rebuild payload (mandatory for adopted games) */
|
|
656
|
+
database?: DatabaseResetDatabasePayload;
|
|
657
|
+
}
|
|
658
|
+
/** Log entry captured from seed worker console output */
|
|
659
|
+
interface SeedLogEntry {
|
|
660
|
+
/** Log level (log, warn, error, info) */
|
|
661
|
+
level: 'log' | 'warn' | 'error' | 'info';
|
|
662
|
+
/** Log message content */
|
|
663
|
+
message: string;
|
|
664
|
+
/** Milliseconds since seed execution started */
|
|
665
|
+
timestamp: number;
|
|
666
|
+
}
|
|
667
|
+
/** Structured error details from D1/SQLite errors */
|
|
668
|
+
interface SeedErrorDetails {
|
|
669
|
+
/** Error category code */
|
|
670
|
+
code?: 'CONSTRAINT_VIOLATION' | 'SQL_ERROR' | 'DATABASE_BUSY';
|
|
671
|
+
/** Table name involved in the error */
|
|
672
|
+
table?: string;
|
|
673
|
+
/** Constraint name or column that caused the error */
|
|
674
|
+
constraint?: string;
|
|
675
|
+
/** Specific constraint type */
|
|
676
|
+
constraintType?: 'UNIQUE' | 'FOREIGN_KEY' | 'NOT_NULL';
|
|
677
|
+
/** Token near syntax error */
|
|
678
|
+
nearToken?: string;
|
|
679
|
+
/** Specific error type within category */
|
|
680
|
+
errorType?: 'TABLE_NOT_FOUND' | 'SYNTAX_ERROR';
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* API response for seed operations (what the API returns to the CLI).
|
|
684
|
+
*
|
|
685
|
+
* Extends the worker response with deployment metadata.
|
|
686
|
+
*/
|
|
687
|
+
interface SeedResponse {
|
|
688
|
+
/** Whether the seed completed successfully */
|
|
689
|
+
success: boolean;
|
|
690
|
+
/** Unique identifier for the seed worker deployment */
|
|
691
|
+
deploymentId: string;
|
|
692
|
+
/** When the seed was executed (ISO 8601 string from JSON serialization) */
|
|
693
|
+
executedAt: string;
|
|
694
|
+
/** Captured console output from the seed script */
|
|
695
|
+
logs?: SeedLogEntry[];
|
|
696
|
+
/** Execution duration in milliseconds */
|
|
697
|
+
duration?: number;
|
|
698
|
+
/** Error message if seed failed */
|
|
699
|
+
error?: string;
|
|
700
|
+
/** Stack trace if seed failed */
|
|
701
|
+
stack?: string;
|
|
702
|
+
/** Structured error details if seed failed */
|
|
703
|
+
details?: SeedErrorDetails;
|
|
704
|
+
}
|
|
705
|
+
interface DomainValidationRecords {
|
|
706
|
+
ownership?: {
|
|
707
|
+
name?: string;
|
|
708
|
+
value?: string;
|
|
709
|
+
type?: string;
|
|
710
|
+
};
|
|
711
|
+
ssl?: {
|
|
712
|
+
txt_name?: string;
|
|
713
|
+
txt_value?: string;
|
|
714
|
+
}[];
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Leaderboard Types
|
|
719
|
+
*
|
|
720
|
+
* @module types/leaderboard
|
|
721
|
+
*/
|
|
722
|
+
type LeaderboardTimeframe = 'all_time' | 'monthly' | 'weekly' | 'daily';
|
|
723
|
+
interface LeaderboardOptions {
|
|
724
|
+
timeframe?: LeaderboardTimeframe;
|
|
725
|
+
limit?: number;
|
|
726
|
+
offset?: number;
|
|
727
|
+
gameId?: string;
|
|
728
|
+
}
|
|
729
|
+
interface LeaderboardEntry {
|
|
730
|
+
rank: number;
|
|
731
|
+
userId: string;
|
|
732
|
+
username: string;
|
|
733
|
+
userImage?: string | null;
|
|
734
|
+
score: number;
|
|
735
|
+
achievedAt: Date;
|
|
736
|
+
metadata?: Record<string, unknown>;
|
|
737
|
+
gameId?: string;
|
|
738
|
+
gameTitle?: string;
|
|
739
|
+
gameSlug?: string;
|
|
740
|
+
}
|
|
741
|
+
interface UserRank {
|
|
742
|
+
rank: number;
|
|
743
|
+
totalPlayers: number;
|
|
744
|
+
score: number;
|
|
745
|
+
percentile: number;
|
|
746
|
+
}
|
|
747
|
+
interface UserRankResponse {
|
|
748
|
+
rank: number;
|
|
749
|
+
score: number;
|
|
750
|
+
userId: string;
|
|
751
|
+
}
|
|
752
|
+
interface UserScore {
|
|
753
|
+
id: string;
|
|
754
|
+
score: number;
|
|
755
|
+
achievedAt: Date;
|
|
756
|
+
metadata?: Record<string, unknown>;
|
|
757
|
+
gameId: string;
|
|
758
|
+
gameTitle: string;
|
|
759
|
+
gameSlug: string;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Leaderboard entry with required game context.
|
|
763
|
+
* Used when fetching leaderboards for a specific game.
|
|
764
|
+
*/
|
|
765
|
+
interface GameLeaderboardEntry {
|
|
766
|
+
rank: number;
|
|
767
|
+
userId: string;
|
|
768
|
+
username: string;
|
|
769
|
+
userImage?: string | null;
|
|
770
|
+
score: number;
|
|
771
|
+
achievedAt: Date;
|
|
772
|
+
metadata?: Record<string, unknown>;
|
|
773
|
+
gameId: string;
|
|
774
|
+
gameTitle: string;
|
|
775
|
+
gameSlug: string;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Skill Status Types
|
|
780
|
+
*
|
|
781
|
+
* Contract for cross-game skill status queries. An answering game exports
|
|
782
|
+
* `getSkillStatus` from `server/lib/skills.ts`; the platform calls its
|
|
783
|
+
* reserved `/__playcademy/skills/status` route on behalf of another game.
|
|
784
|
+
*
|
|
785
|
+
* @module types/skills
|
|
786
|
+
*/
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Current status of one skill for one student, as judged by the answering game.
|
|
790
|
+
*
|
|
791
|
+
* - `locked`: the student can't work on the skill yet in this game.
|
|
792
|
+
* - `unlocked`: available to the student, but no progress yet.
|
|
793
|
+
* - `in_progress`: started, not mastered.
|
|
794
|
+
* - `mastered`: currently meets this game's mastery bar, including by placement.
|
|
795
|
+
* - `unknown_skill`: the skill ID isn't recognized. Never report an unrecognized
|
|
796
|
+
* skill as `locked`.
|
|
797
|
+
*/
|
|
798
|
+
type SkillStatus = (typeof SKILL_STATUSES)[number];
|
|
799
|
+
/** How a `mastered` status was reached. */
|
|
800
|
+
type SkillMasterySource = (typeof SKILL_MASTERY_SOURCES)[number];
|
|
801
|
+
interface SkillStatusResult {
|
|
802
|
+
/** Skill ID, lowercase. */
|
|
803
|
+
skill: string;
|
|
804
|
+
/** Current status. */
|
|
805
|
+
status: SkillStatus;
|
|
806
|
+
/** How mastery was reached. Only allowed when `status` is `mastered`. */
|
|
807
|
+
source?: SkillMasterySource;
|
|
808
|
+
/** ISO 8601 time of the evidence behind this status. */
|
|
809
|
+
asOf?: string;
|
|
810
|
+
}
|
|
811
|
+
/** Response returned by an answering game's `getSkillStatus` handler. */
|
|
812
|
+
interface SkillStatusResponse {
|
|
813
|
+
/** Student identifier (Playcademy user ID). Must match the requested student. */
|
|
814
|
+
studentId: string;
|
|
815
|
+
/** One result per skill the handler was asked about. */
|
|
816
|
+
results: SkillStatusResult[];
|
|
817
|
+
}
|
|
818
|
+
/** The game asking for skill status, as identified by the platform. */
|
|
819
|
+
interface SkillStatusRequestingGame {
|
|
820
|
+
id: string;
|
|
821
|
+
slug: string;
|
|
822
|
+
}
|
|
823
|
+
/** Body of `POST /api/skills/status`, sent by the asking game's worker. */
|
|
824
|
+
interface SkillStatusRequest {
|
|
825
|
+
/** Slug of the game to ask. */
|
|
826
|
+
game: string;
|
|
827
|
+
/** Playcademy user ID of the student. */
|
|
828
|
+
studentId: string;
|
|
829
|
+
/** Skill IDs to ask about. Normalized (trimmed, lowercased) and de-duplicated. */
|
|
830
|
+
skills: string[];
|
|
831
|
+
}
|
|
832
|
+
/** Why the answering game couldn't answer. */
|
|
833
|
+
type SkillStatusUnsupportedReason = 'not_implemented' | 'no_active_deployment' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
834
|
+
/**
|
|
835
|
+
* What the asking game gets back. When `supported` is false, fall back to
|
|
836
|
+
* the game's own default (usually: treat the skills as not mastered).
|
|
837
|
+
*/
|
|
838
|
+
type SkillStatusLookupResult = {
|
|
839
|
+
supported: true;
|
|
840
|
+
results: SkillStatusResult[];
|
|
841
|
+
} | {
|
|
842
|
+
supported: false;
|
|
843
|
+
reason: SkillStatusUnsupportedReason;
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* TimeBack Enums & Literal Types
|
|
848
|
+
*
|
|
849
|
+
* Basic type definitions used throughout the TimeBack integration. Unions
|
|
850
|
+
* derive from the canonical value lists in @playcademy/constants (type-only
|
|
851
|
+
* imports, so this package ships no runtime code).
|
|
852
|
+
*
|
|
853
|
+
* @module types/timeback/types
|
|
854
|
+
*/
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* The TimeBack subject vocabulary. 'None' is canonical: TimeBack accepts it
|
|
858
|
+
* on courses and uses it as the fallback for unknown or missing subjects.
|
|
859
|
+
*/
|
|
860
|
+
type TimebackSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
861
|
+
/**
|
|
862
|
+
* Grade levels per AE OneRoster GradeEnum.
|
|
863
|
+
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
864
|
+
*/
|
|
865
|
+
type TimebackGrade = (typeof TIMEBACK_GRADES)[number];
|
|
866
|
+
/**
|
|
867
|
+
* Platform pedagogy levels for a single lesson of instruction.
|
|
868
|
+
* E1 teaches, E2/E3 practice and remediate, E4 is the mastery check.
|
|
869
|
+
*/
|
|
870
|
+
type ELevel = 'E1' | 'E2' | 'E3' | 'E4';
|
|
871
|
+
/**
|
|
872
|
+
* Valid Caliper subject values. The same vocabulary as OneRoster subjects.
|
|
873
|
+
*/
|
|
874
|
+
type CaliperSubject = (typeof TIMEBACK_SUBJECTS)[number];
|
|
875
|
+
/**
|
|
876
|
+
* OneRoster organization types.
|
|
877
|
+
*/
|
|
878
|
+
type OrganizationType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
879
|
+
/**
|
|
880
|
+
* Lesson types for PowerPath integration.
|
|
881
|
+
*/
|
|
882
|
+
type LessonType = 'powerpath-100' | 'quiz' | 'test-out' | 'placement' | 'unit-test' | 'alpha-read-article' | null;
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* TimeBack Configuration Types
|
|
886
|
+
*
|
|
887
|
+
* Configuration interfaces for Organization, Course, Component,
|
|
888
|
+
* Resource, and complete TimeBack setup.
|
|
889
|
+
*
|
|
890
|
+
* @module types/timeback/config
|
|
891
|
+
*/
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Organization configuration for TimeBack (user input - optionals allowed)
|
|
895
|
+
*/
|
|
896
|
+
interface OrganizationConfig {
|
|
897
|
+
/** Display name for your organization */
|
|
898
|
+
name?: string;
|
|
899
|
+
/** Organization type */
|
|
900
|
+
type?: OrganizationType;
|
|
901
|
+
/** Unique identifier (defaults to Playcademy's org) */
|
|
902
|
+
identifier?: string;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Course goals for daily student targets
|
|
906
|
+
*/
|
|
907
|
+
interface CourseGoals {
|
|
908
|
+
/** Target XP students should earn per day */
|
|
909
|
+
dailyXp?: number;
|
|
910
|
+
/** Target lessons per day */
|
|
911
|
+
dailyLessons?: number;
|
|
912
|
+
/** Target active minutes per day */
|
|
913
|
+
dailyActiveMinutes?: number;
|
|
914
|
+
/** Target accuracy percentage */
|
|
915
|
+
dailyAccuracy?: number;
|
|
916
|
+
/** Target mastered units per day */
|
|
917
|
+
dailyMasteredUnits?: number;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Course metrics and totals
|
|
921
|
+
*/
|
|
922
|
+
interface CourseMetrics {
|
|
923
|
+
/** Total XP available in the course */
|
|
924
|
+
totalXp?: number;
|
|
925
|
+
/** Total lessons/activities in the course */
|
|
926
|
+
totalLessons?: number;
|
|
927
|
+
/** Total number of grade levels covered by this course */
|
|
928
|
+
totalGrades?: number;
|
|
929
|
+
/** The type of course (e.g. 'optional', 'hole-filling', 'base') */
|
|
930
|
+
courseType?: 'base' | 'hole-filling' | 'optional' | 'Base' | 'Hole-Filling' | 'Optional';
|
|
931
|
+
/** Indicates whether the course is supplemental content */
|
|
932
|
+
isSupplemental?: boolean;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Complete course metadata structure
|
|
936
|
+
*/
|
|
937
|
+
interface CourseMetadata {
|
|
938
|
+
/** Define the type of course and priority for the student */
|
|
939
|
+
courseType?: 'base' | 'hole-filling' | 'optional';
|
|
940
|
+
/** Boolean value to determine if a course is supplemental to a base course */
|
|
941
|
+
isSupplemental?: boolean;
|
|
942
|
+
/** Boolean value to determine if a course is custom to an individual student */
|
|
943
|
+
isCustom?: boolean;
|
|
944
|
+
/** Signals whether a course is in production with students */
|
|
945
|
+
publishStatus?: 'draft' | 'testing' | 'published' | 'deactivated';
|
|
946
|
+
/** Whether this course appears in the TimeBack catalog for teachers and parents */
|
|
947
|
+
timebackVisible?: boolean;
|
|
948
|
+
/** Who to contact when issues reported with questions */
|
|
949
|
+
contactEmail?: string;
|
|
950
|
+
/** Primary app identifier */
|
|
951
|
+
primaryApp?: string;
|
|
952
|
+
/** Learning goals for students */
|
|
953
|
+
goals?: CourseGoals;
|
|
954
|
+
/** Course metrics and totals */
|
|
955
|
+
metrics?: CourseMetrics;
|
|
956
|
+
/** Vendor-specific metadata (e.g., AlphaLearn) */
|
|
957
|
+
[key: string]: unknown;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Course configuration for TimeBack (user input)
|
|
961
|
+
*/
|
|
962
|
+
interface CourseConfig {
|
|
963
|
+
/** Allocated OneRoster sourcedId (set after creation) */
|
|
964
|
+
sourcedId?: string;
|
|
965
|
+
/** Course title (defaults to game name) */
|
|
966
|
+
title?: string;
|
|
967
|
+
/** Subjects (REQUIRED for TimeBack integration) */
|
|
968
|
+
subjects: TimebackSubject[];
|
|
969
|
+
/** Used when recording progress/sessions if not explicitly specified per event. */
|
|
970
|
+
defaultSubject?: TimebackSubject;
|
|
971
|
+
/** Grade levels (REQUIRED for TimeBack integration) */
|
|
972
|
+
grades: TimebackGrade[];
|
|
973
|
+
/** Short course code (optional, auto-generated) */
|
|
974
|
+
courseCode?: string;
|
|
975
|
+
/** Course level (auto-derived from grades) */
|
|
976
|
+
level?: 'Elementary' | 'Middle' | 'High' | 'AP' | string;
|
|
977
|
+
/** Grading system */
|
|
978
|
+
gradingScheme?: 'STANDARD';
|
|
979
|
+
/** Total XP available in this course (REQUIRED before setup) */
|
|
980
|
+
totalXp?: number | null;
|
|
981
|
+
/** Total masterable units in this course (REQUIRED before setup) */
|
|
982
|
+
masterableUnits?: number | null;
|
|
983
|
+
/** Custom Playcademy metadata */
|
|
984
|
+
metadata?: CourseMetadata;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Component configuration for TimeBack (user input)
|
|
988
|
+
*/
|
|
989
|
+
interface ComponentConfig {
|
|
990
|
+
/** Component title (defaults to "{course.title} Activities") */
|
|
991
|
+
title?: string;
|
|
992
|
+
/** Display order */
|
|
993
|
+
sortOrder?: number;
|
|
994
|
+
/** Required prior components */
|
|
995
|
+
prerequisites?: string[];
|
|
996
|
+
/** How prerequisites work */
|
|
997
|
+
prerequisiteCriteria?: 'ALL' | 'ANY';
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Playcademy-specific resource extensions
|
|
1001
|
+
*/
|
|
1002
|
+
interface PlaycademyResourceMetadata {
|
|
1003
|
+
/** Mastery configuration for tracking discrete learning units */
|
|
1004
|
+
mastery?: {
|
|
1005
|
+
/** Total number of masterable units in the resource */
|
|
1006
|
+
masterableUnits: number;
|
|
1007
|
+
/** Type of mastery unit for semantic clarity */
|
|
1008
|
+
unitType?: 'level' | 'rank' | 'skill' | 'module';
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Resource configuration for TimeBack (user input)
|
|
1013
|
+
*/
|
|
1014
|
+
interface ResourceConfig {
|
|
1015
|
+
/** Resource title (defaults to "{course.title} Game") */
|
|
1016
|
+
title?: string;
|
|
1017
|
+
/** Internal resource ID (auto-generated from package.json) */
|
|
1018
|
+
vendorResourceId?: string;
|
|
1019
|
+
/** Vendor identifier */
|
|
1020
|
+
vendorId?: string;
|
|
1021
|
+
/** Application identifier */
|
|
1022
|
+
applicationId?: string;
|
|
1023
|
+
/** Resource roles */
|
|
1024
|
+
roles?: ('primary' | 'secondary')[];
|
|
1025
|
+
/** Resource importance */
|
|
1026
|
+
importance?: 'primary' | 'secondary';
|
|
1027
|
+
/** Interactive resource metadata */
|
|
1028
|
+
metadata?: {
|
|
1029
|
+
/** Resource type */
|
|
1030
|
+
type?: 'interactive';
|
|
1031
|
+
/** Launch URL (defaults to Playcademy game URL) */
|
|
1032
|
+
launchUrl?: string;
|
|
1033
|
+
/** Platform name */
|
|
1034
|
+
toolProvider?: string;
|
|
1035
|
+
/** Teaching method */
|
|
1036
|
+
instructionalMethod?: 'exploratory' | 'direct-instruction';
|
|
1037
|
+
/** Subject area */
|
|
1038
|
+
subject?: TimebackSubject;
|
|
1039
|
+
/** Target grades */
|
|
1040
|
+
grades?: TimebackGrade[];
|
|
1041
|
+
/** Content language */
|
|
1042
|
+
language?: string;
|
|
1043
|
+
/** Base XP for completion */
|
|
1044
|
+
xp?: number;
|
|
1045
|
+
/** Playcademy-specific extensions */
|
|
1046
|
+
playcademy?: PlaycademyResourceMetadata;
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Component Resource link configuration (user input)
|
|
1051
|
+
*/
|
|
1052
|
+
interface ComponentResourceConfig {
|
|
1053
|
+
/** Link title (defaults to "{resource.title} Activity") */
|
|
1054
|
+
title?: string;
|
|
1055
|
+
/** Display order */
|
|
1056
|
+
sortOrder?: number;
|
|
1057
|
+
/** Lesson type for PowerPath integration */
|
|
1058
|
+
lessonType?: LessonType;
|
|
1059
|
+
}
|
|
1060
|
+
interface TimebackCourseConfig {
|
|
1061
|
+
subject: string;
|
|
1062
|
+
grade: number;
|
|
1063
|
+
}
|
|
1064
|
+
interface DerivedPlatformCourseConfig extends TimebackCourseConfig {
|
|
1065
|
+
title: string;
|
|
1066
|
+
courseCode: string;
|
|
1067
|
+
level: string;
|
|
1068
|
+
metadata?: Record<string, unknown>;
|
|
1069
|
+
totalXp?: number | null;
|
|
1070
|
+
masterableUnits?: number | null;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* TimeBack Client SDK DTOs
|
|
1075
|
+
*
|
|
1076
|
+
* Data transfer objects for the TimeBack client SDK including
|
|
1077
|
+
* progress tracking, session management, and activity completion.
|
|
1078
|
+
*
|
|
1079
|
+
* Note: TimebackClientConfig lives in @playcademy/timeback as it's
|
|
1080
|
+
* SDK configuration, not a DTO.
|
|
1081
|
+
*
|
|
1082
|
+
* @module types/timeback/client
|
|
1083
|
+
*/
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Known extensions for TimeBack Activity Metrics Collection
|
|
1087
|
+
*/
|
|
1088
|
+
interface TimebackActivityExtensions {
|
|
1089
|
+
/** Percentage complete (0-100) for the app course */
|
|
1090
|
+
pctCompleteApp?: number;
|
|
1091
|
+
/** Allow other arbitrary extensions */
|
|
1092
|
+
[key: string]: unknown;
|
|
1093
|
+
}
|
|
1094
|
+
interface MasteryWriteWarning {
|
|
1095
|
+
code: 'MASTERY_WRITE_CAPPED';
|
|
1096
|
+
message: string;
|
|
1097
|
+
currentMasteredUnits: number;
|
|
1098
|
+
attemptedMasteredUnits: number;
|
|
1099
|
+
appliedMasteredUnits: number;
|
|
1100
|
+
storedMasteredUnits: number;
|
|
1101
|
+
masterableUnits: number;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Activity data for ending an activity
|
|
1105
|
+
*/
|
|
1106
|
+
interface ActivityData {
|
|
1107
|
+
/** Unique activity identifier (required) */
|
|
1108
|
+
activityId: string;
|
|
1109
|
+
/** Grade level for this activity (required for multi-grade course routing) */
|
|
1110
|
+
grade: number;
|
|
1111
|
+
/** Subject area (required for multi-grade course routing) */
|
|
1112
|
+
subject: CaliperSubject;
|
|
1113
|
+
/** Activity display name (optional) */
|
|
1114
|
+
activityName?: string;
|
|
1115
|
+
/** Course identifier (auto-filled from config if not provided) */
|
|
1116
|
+
courseId?: string;
|
|
1117
|
+
/** Course display name (auto-filled from config if not provided) */
|
|
1118
|
+
courseName?: string;
|
|
1119
|
+
/** Student email address (optional) */
|
|
1120
|
+
studentEmail?: string;
|
|
1121
|
+
/** Application name for Caliper events (defaults to 'Game') */
|
|
1122
|
+
appName?: string;
|
|
1123
|
+
/** Sensor URL for Caliper events (defaults to baseUrl) */
|
|
1124
|
+
sensorUrl?: string;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Score data for ending an activity
|
|
1128
|
+
*/
|
|
1129
|
+
interface EndActivityScoreData {
|
|
1130
|
+
/** Number of questions answered correctly */
|
|
1131
|
+
correctQuestions: number;
|
|
1132
|
+
/** Total number of questions */
|
|
1133
|
+
totalQuestions: number;
|
|
1134
|
+
/**
|
|
1135
|
+
* XP earned for this activity.
|
|
1136
|
+
*
|
|
1137
|
+
* Required when the game reports directly (platform, demo, standalone).
|
|
1138
|
+
* A child-launched game may omit it: narrow the client with the SDK's
|
|
1139
|
+
* `isChildLaunched()` for the relaxed signature. The parent game
|
|
1140
|
+
* decides the award, and a value provided here travels only as the
|
|
1141
|
+
* `childXpSuggested` audit suggestion.
|
|
1142
|
+
*/
|
|
1143
|
+
xpAwarded: number;
|
|
1144
|
+
/**
|
|
1145
|
+
* Incremental learning units mastered this session.
|
|
1146
|
+
*
|
|
1147
|
+
* Cannot be used with masteredUnitsAbsolute. When a course has mastery
|
|
1148
|
+
* tracking configured, the platform bounds submissions that would move
|
|
1149
|
+
* the student below 0 or above the configured masterableUnits maximum and
|
|
1150
|
+
* returns warning metadata.
|
|
1151
|
+
*/
|
|
1152
|
+
masteredUnits?: number;
|
|
1153
|
+
/**
|
|
1154
|
+
* Absolute total of mastered units.
|
|
1155
|
+
*
|
|
1156
|
+
* The platform computes the delta from the current value. Cannot be used
|
|
1157
|
+
* with masteredUnits. When a course has mastery tracking configured, the
|
|
1158
|
+
* platform bounds values outside 0..masterableUnits and returns warning
|
|
1159
|
+
* metadata.
|
|
1160
|
+
*/
|
|
1161
|
+
masteredUnitsAbsolute?: number;
|
|
1162
|
+
/** Optional arbitrary extensions to include in the Caliper event */
|
|
1163
|
+
extensions?: TimebackActivityExtensions;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Caliper Protocol Types
|
|
1168
|
+
*
|
|
1169
|
+
* Types for the IMS Caliper Analytics standard used by TimeBack
|
|
1170
|
+
* for learning activity tracking.
|
|
1171
|
+
*
|
|
1172
|
+
* @module types/timeback/caliper
|
|
1173
|
+
*/
|
|
1174
|
+
|
|
1175
|
+
interface TimebackStoredCaliperMetric {
|
|
1176
|
+
type: string;
|
|
1177
|
+
value?: number;
|
|
1178
|
+
subType?: string;
|
|
1179
|
+
}
|
|
1180
|
+
interface TimebackStoredCaliperEntity {
|
|
1181
|
+
id?: string;
|
|
1182
|
+
type?: string;
|
|
1183
|
+
name?: string;
|
|
1184
|
+
email?: string;
|
|
1185
|
+
app?: {
|
|
1186
|
+
id?: string;
|
|
1187
|
+
name?: string;
|
|
1188
|
+
};
|
|
1189
|
+
activity?: {
|
|
1190
|
+
id?: string;
|
|
1191
|
+
name?: string;
|
|
1192
|
+
extensions?: Record<string, unknown> | null;
|
|
1193
|
+
};
|
|
1194
|
+
course?: {
|
|
1195
|
+
id?: string;
|
|
1196
|
+
name?: string;
|
|
1197
|
+
};
|
|
1198
|
+
items?: TimebackStoredCaliperMetric[];
|
|
1199
|
+
extensions?: Record<string, unknown> | null;
|
|
1200
|
+
[key: string]: unknown;
|
|
1201
|
+
}
|
|
1202
|
+
interface TimebackStoredCaliperEvent {
|
|
1203
|
+
id: number;
|
|
1204
|
+
externalId: string;
|
|
1205
|
+
sensor: string;
|
|
1206
|
+
type: string;
|
|
1207
|
+
profile?: string;
|
|
1208
|
+
action: string;
|
|
1209
|
+
eventTime: string;
|
|
1210
|
+
sendTime: string;
|
|
1211
|
+
updated_at: string | null;
|
|
1212
|
+
created_at: string;
|
|
1213
|
+
deleted_at: string | null;
|
|
1214
|
+
actor: TimebackStoredCaliperEntity;
|
|
1215
|
+
object: TimebackStoredCaliperEntity;
|
|
1216
|
+
generated?: TimebackStoredCaliperEntity | null;
|
|
1217
|
+
target?: TimebackStoredCaliperEntity | null;
|
|
1218
|
+
referrer?: TimebackStoredCaliperEntity | null;
|
|
1219
|
+
edApp?: TimebackStoredCaliperEntity | null;
|
|
1220
|
+
group?: TimebackStoredCaliperEntity | null;
|
|
1221
|
+
membership?: TimebackStoredCaliperEntity | null;
|
|
1222
|
+
session?: TimebackStoredCaliperEntity | null;
|
|
1223
|
+
federatedSession?: TimebackStoredCaliperEntity | null;
|
|
1224
|
+
extensions?: Record<string, unknown> | null;
|
|
1225
|
+
clientAppId?: string | null;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Game-side learning metrics for a single student.
|
|
1230
|
+
*
|
|
1231
|
+
* Returned by opted-in game workers from their reserved
|
|
1232
|
+
* `/__playcademy/metrics` route.
|
|
1233
|
+
*/
|
|
1234
|
+
interface GameMetricsResponse {
|
|
1235
|
+
/** Student identifier (Playcademy user ID). */
|
|
1236
|
+
studentId: string;
|
|
1237
|
+
/** Student email, when available to the game. */
|
|
1238
|
+
email?: string;
|
|
1239
|
+
/** Per-course metrics grouped by TimeBack grade and subject. */
|
|
1240
|
+
courses: GameCourseMetrics[];
|
|
1241
|
+
}
|
|
1242
|
+
interface GameCourseMetrics {
|
|
1243
|
+
/** Grade level matching the game's TimeBack integration. */
|
|
1244
|
+
grade: TimebackGrade;
|
|
1245
|
+
/** Subject matching the game's TimeBack integration. */
|
|
1246
|
+
subject: TimebackSubject;
|
|
1247
|
+
/** Total XP the game has recorded for this student in this course. */
|
|
1248
|
+
totalXp?: number;
|
|
1249
|
+
/** Mastered units the game has recorded for this course. */
|
|
1250
|
+
masteredUnits?: number;
|
|
1251
|
+
/** Total active time in seconds the game has recorded for this course. */
|
|
1252
|
+
activeTimeSeconds?: number;
|
|
1253
|
+
/** Optional per-run breakdown keyed by Timeback run ID. */
|
|
1254
|
+
activities?: GameRunMetrics[];
|
|
1255
|
+
}
|
|
1256
|
+
interface GameRunMetrics {
|
|
1257
|
+
runId: string;
|
|
1258
|
+
activityId: string;
|
|
1259
|
+
activityName?: string;
|
|
1260
|
+
totalXp?: number;
|
|
1261
|
+
masteredUnits?: number;
|
|
1262
|
+
activeTimeSeconds?: number;
|
|
1263
|
+
score?: number;
|
|
1264
|
+
}
|
|
1265
|
+
type GameMetricsUnsupportedReason = 'no_timeback_integration' | 'no_active_deployment' | 'route_not_implemented' | 'timeout' | 'fetch_failed' | 'invalid_response';
|
|
1266
|
+
type GameMetricsProxyResponse = {
|
|
1267
|
+
supported: true;
|
|
1268
|
+
metrics: GameMetricsResponse;
|
|
1269
|
+
} | {
|
|
1270
|
+
supported: false;
|
|
1271
|
+
reason: GameMetricsUnsupportedReason;
|
|
1272
|
+
details?: string;
|
|
1273
|
+
};
|
|
1274
|
+
type GameMetricComparisonMetric = 'xp' | 'mastery' | 'time' | 'score';
|
|
1275
|
+
type GameMetricComparisonKind = 'number' | 'time' | 'percent';
|
|
1276
|
+
type GameMetricComparisonRowStatus = 'matched' | 'discrepant' | 'not_reported_by_game' | 'not_recorded_by_timeback';
|
|
1277
|
+
interface GameMetricComparisonRow {
|
|
1278
|
+
metric: GameMetricComparisonMetric;
|
|
1279
|
+
kind: GameMetricComparisonKind;
|
|
1280
|
+
status: GameMetricComparisonRowStatus;
|
|
1281
|
+
timebackValue?: number;
|
|
1282
|
+
gameValue?: number;
|
|
1283
|
+
delta?: number;
|
|
1284
|
+
}
|
|
1285
|
+
type GameRunMetricsComparisonStatus = 'matched' | 'discrepant' | 'not_reported' | 'unavailable';
|
|
1286
|
+
interface GameRunMetricsComparisonSummary {
|
|
1287
|
+
runId: string;
|
|
1288
|
+
status: GameRunMetricsComparisonStatus;
|
|
1289
|
+
discrepancyCount: number;
|
|
1290
|
+
reason?: GameMetricsUnsupportedReason;
|
|
1291
|
+
}
|
|
1292
|
+
interface GameRunMetricsComparison extends GameRunMetricsComparisonSummary {
|
|
1293
|
+
rows: GameMetricComparisonRow[];
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* QTI API Response Types
|
|
1298
|
+
*
|
|
1299
|
+
* Types for the QTI assessment content service responses.
|
|
1300
|
+
* These mirror the JSON structure returned by the QTI API
|
|
1301
|
+
* (beyond-qti-2) for assessment tests and items.
|
|
1302
|
+
*
|
|
1303
|
+
* @module types/timeback/qti
|
|
1304
|
+
*/
|
|
1305
|
+
interface QtiItemRef {
|
|
1306
|
+
identifier: string;
|
|
1307
|
+
href: string;
|
|
1308
|
+
sequence?: number;
|
|
1309
|
+
}
|
|
1310
|
+
interface QtiAssessmentSection {
|
|
1311
|
+
identifier: string;
|
|
1312
|
+
title: string;
|
|
1313
|
+
visible?: boolean;
|
|
1314
|
+
required?: boolean;
|
|
1315
|
+
fixed?: boolean;
|
|
1316
|
+
sequence?: number;
|
|
1317
|
+
'qti-assessment-item-ref'?: QtiItemRef[];
|
|
1318
|
+
}
|
|
1319
|
+
interface QtiTestPart {
|
|
1320
|
+
identifier: string;
|
|
1321
|
+
navigationMode: 'linear' | 'nonlinear';
|
|
1322
|
+
submissionMode: 'individual' | 'simultaneous';
|
|
1323
|
+
'qti-assessment-section': QtiAssessmentSection[];
|
|
1324
|
+
}
|
|
1325
|
+
interface QtiOutcomeDeclaration {
|
|
1326
|
+
identifier: string;
|
|
1327
|
+
cardinality?: 'single' | 'multiple' | 'ordered' | 'record';
|
|
1328
|
+
baseType: 'boolean' | 'directedPair' | 'duration' | 'file' | 'float' | 'identifier' | 'integer' | 'pair' | 'point' | 'string' | 'uri';
|
|
1329
|
+
normalMaximum?: number;
|
|
1330
|
+
normalMinimum?: number;
|
|
1331
|
+
defaultValue?: {
|
|
1332
|
+
value?: unknown;
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
interface QtiAssessmentTest {
|
|
1336
|
+
identifier: string;
|
|
1337
|
+
title: string;
|
|
1338
|
+
qtiVersion?: string;
|
|
1339
|
+
timeLimit?: number;
|
|
1340
|
+
maxAttempts?: number;
|
|
1341
|
+
toolsEnabled?: Record<string, boolean>;
|
|
1342
|
+
'qti-test-part': QtiTestPart[];
|
|
1343
|
+
'qti-outcome-declaration'?: QtiOutcomeDeclaration[];
|
|
1344
|
+
metadata?: Record<string, unknown>;
|
|
1345
|
+
rawXml?: string;
|
|
1346
|
+
content?: Record<string, unknown>;
|
|
1347
|
+
createdAt?: string;
|
|
1348
|
+
updatedAt?: string;
|
|
1349
|
+
}
|
|
1350
|
+
interface QtiAssessmentTestListResponse {
|
|
1351
|
+
items: QtiAssessmentTest[];
|
|
1352
|
+
total: number;
|
|
1353
|
+
page: number;
|
|
1354
|
+
pages: number;
|
|
1355
|
+
limit: number;
|
|
1356
|
+
sort: string;
|
|
1357
|
+
order: 'asc' | 'desc';
|
|
1358
|
+
}
|
|
1359
|
+
interface QtiResponseDeclaration {
|
|
1360
|
+
identifier: string;
|
|
1361
|
+
cardinality: 'single' | 'multiple' | 'ordered';
|
|
1362
|
+
baseType: string;
|
|
1363
|
+
correctResponse?: {
|
|
1364
|
+
value: string[];
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
interface QtiAssessmentItem {
|
|
1368
|
+
identifier: string;
|
|
1369
|
+
title: string;
|
|
1370
|
+
type?: string;
|
|
1371
|
+
qtiVersion?: string;
|
|
1372
|
+
timeDependent?: boolean;
|
|
1373
|
+
adaptive?: boolean;
|
|
1374
|
+
preInteraction?: string;
|
|
1375
|
+
interaction?: Record<string, unknown>;
|
|
1376
|
+
postInteraction?: string;
|
|
1377
|
+
responseDeclarations?: QtiResponseDeclaration[];
|
|
1378
|
+
outcomeDeclarations?: QtiOutcomeDeclaration[];
|
|
1379
|
+
responseProcessing?: Record<string, unknown>;
|
|
1380
|
+
metadata?: Record<string, unknown>;
|
|
1381
|
+
modalFeedback?: unknown[];
|
|
1382
|
+
feedbackInline?: unknown[];
|
|
1383
|
+
feedbackBlock?: unknown[];
|
|
1384
|
+
rubrics?: unknown[];
|
|
1385
|
+
stimulus?: unknown;
|
|
1386
|
+
rawXml?: string;
|
|
1387
|
+
content?: Record<string, unknown>;
|
|
1388
|
+
createdAt?: string;
|
|
1389
|
+
updatedAt?: string;
|
|
1390
|
+
}
|
|
1391
|
+
interface QtiAssessmentItemListResponse {
|
|
1392
|
+
items: QtiAssessmentItem[];
|
|
1393
|
+
total: number;
|
|
1394
|
+
page: number;
|
|
1395
|
+
pages: number;
|
|
1396
|
+
limit: number;
|
|
1397
|
+
sort: string;
|
|
1398
|
+
order: 'asc' | 'desc';
|
|
1399
|
+
}
|
|
1400
|
+
/** Request an exact, independently owned copy through the normal question collection endpoint. */
|
|
1401
|
+
interface QtiQuestionCopyCreateInput {
|
|
1402
|
+
mode: 'copy';
|
|
1403
|
+
sourceItemIdentifier: string;
|
|
1404
|
+
}
|
|
1405
|
+
type QtiQuestionCreateInput = QtiQuestionCopyCreateInput | Record<string, unknown>;
|
|
1406
|
+
interface QtiTestQuestionRef {
|
|
1407
|
+
ownership?: 'owned' | 'shared';
|
|
1408
|
+
reference: {
|
|
1409
|
+
identifier: string;
|
|
1410
|
+
href: string;
|
|
1411
|
+
testPart: string;
|
|
1412
|
+
section: string;
|
|
1413
|
+
};
|
|
1414
|
+
question: QtiAssessmentItem;
|
|
1415
|
+
}
|
|
1416
|
+
interface QtiTestQuestionsResponse {
|
|
1417
|
+
assessmentTest: string;
|
|
1418
|
+
title: string;
|
|
1419
|
+
totalQuestions: number;
|
|
1420
|
+
questions: QtiTestQuestionRef[];
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* TimeBack API Request/Response Types
|
|
1425
|
+
*
|
|
1426
|
+
* Types for TimeBack API endpoints including XP tracking,
|
|
1427
|
+
* setup, verification, and activity completion.
|
|
1428
|
+
*
|
|
1429
|
+
* @module types/timeback/api
|
|
1430
|
+
*/
|
|
1431
|
+
|
|
1432
|
+
interface PopulateStudentResponse {
|
|
1433
|
+
status: string;
|
|
1434
|
+
message?: string;
|
|
1435
|
+
}
|
|
1436
|
+
interface TimebackSetupRequest {
|
|
1437
|
+
gameId: string;
|
|
1438
|
+
config: {
|
|
1439
|
+
organization: {
|
|
1440
|
+
name: string;
|
|
1441
|
+
type: string;
|
|
1442
|
+
identifier: string;
|
|
1443
|
+
};
|
|
1444
|
+
course: {
|
|
1445
|
+
title: string;
|
|
1446
|
+
subjects: string[];
|
|
1447
|
+
grades: number[];
|
|
1448
|
+
courseCode: string;
|
|
1449
|
+
level: string;
|
|
1450
|
+
gradingScheme: string;
|
|
1451
|
+
metadata?: Record<string, unknown>;
|
|
1452
|
+
};
|
|
1453
|
+
component: {
|
|
1454
|
+
title: string;
|
|
1455
|
+
sortOrder: number;
|
|
1456
|
+
prerequisites: string[];
|
|
1457
|
+
prerequisiteCriteria: string;
|
|
1458
|
+
};
|
|
1459
|
+
resource: {
|
|
1460
|
+
title: string;
|
|
1461
|
+
vendorResourceId: string;
|
|
1462
|
+
vendorId: string;
|
|
1463
|
+
applicationId: string;
|
|
1464
|
+
roles: string[];
|
|
1465
|
+
importance: string;
|
|
1466
|
+
metadata: {
|
|
1467
|
+
type?: string;
|
|
1468
|
+
launchUrl?: string;
|
|
1469
|
+
toolProvider?: string;
|
|
1470
|
+
instructionalMethod?: string;
|
|
1471
|
+
subject?: string;
|
|
1472
|
+
grades?: number[];
|
|
1473
|
+
language?: string;
|
|
1474
|
+
xp?: number;
|
|
1475
|
+
[key: string]: unknown;
|
|
1476
|
+
};
|
|
1477
|
+
};
|
|
1478
|
+
componentResource: {
|
|
1479
|
+
title: string;
|
|
1480
|
+
sortOrder: number;
|
|
1481
|
+
lessonType: string | null;
|
|
1482
|
+
};
|
|
1483
|
+
};
|
|
1484
|
+
verbose?: boolean;
|
|
1485
|
+
}
|
|
1486
|
+
interface PlatformTimebackSetupRequest {
|
|
1487
|
+
gameId: string;
|
|
1488
|
+
courses: DerivedPlatformCourseConfig[];
|
|
1489
|
+
baseConfig: {
|
|
1490
|
+
organization: {
|
|
1491
|
+
name: string;
|
|
1492
|
+
type: string;
|
|
1493
|
+
identifier: string;
|
|
1494
|
+
};
|
|
1495
|
+
component: {
|
|
1496
|
+
title: string;
|
|
1497
|
+
titleSuffix?: string;
|
|
1498
|
+
sortOrder: number;
|
|
1499
|
+
prerequisites: string[];
|
|
1500
|
+
prerequisiteCriteria: string;
|
|
1501
|
+
};
|
|
1502
|
+
resource: {
|
|
1503
|
+
title: string;
|
|
1504
|
+
titleSuffix?: string;
|
|
1505
|
+
vendorResourceId: string;
|
|
1506
|
+
vendorId: string;
|
|
1507
|
+
applicationId: string;
|
|
1508
|
+
roles: string[];
|
|
1509
|
+
importance: string;
|
|
1510
|
+
metadata: {
|
|
1511
|
+
type?: string;
|
|
1512
|
+
launchUrl?: string;
|
|
1513
|
+
toolProvider?: string;
|
|
1514
|
+
instructionalMethod?: string;
|
|
1515
|
+
language?: string;
|
|
1516
|
+
[key: string]: unknown;
|
|
1517
|
+
};
|
|
1518
|
+
};
|
|
1519
|
+
componentResource: {
|
|
1520
|
+
title: string;
|
|
1521
|
+
titleSuffix?: string;
|
|
1522
|
+
sortOrder: number;
|
|
1523
|
+
lessonType: string | null;
|
|
1524
|
+
};
|
|
1525
|
+
};
|
|
1526
|
+
verbose?: boolean;
|
|
1527
|
+
}
|
|
1528
|
+
interface PlatformTimebackSetupResponse {
|
|
1529
|
+
integrations: GameTimebackIntegration[];
|
|
1530
|
+
verbose?: {
|
|
1531
|
+
integration: GameTimebackIntegration;
|
|
1532
|
+
config: {
|
|
1533
|
+
course: unknown;
|
|
1534
|
+
component: unknown;
|
|
1535
|
+
resource: unknown;
|
|
1536
|
+
componentResource: unknown;
|
|
1537
|
+
};
|
|
1538
|
+
}[];
|
|
1539
|
+
}
|
|
1540
|
+
interface GameTimebackIntegration {
|
|
1541
|
+
id: string;
|
|
1542
|
+
gameId: string;
|
|
1543
|
+
courseId: string;
|
|
1544
|
+
grade: number;
|
|
1545
|
+
subject: string;
|
|
1546
|
+
status?: 'active' | 'deactivated';
|
|
1547
|
+
totalXp?: number | null;
|
|
1548
|
+
lastVerifiedAt?: Date | null;
|
|
1549
|
+
deactivatedAt?: Date | null;
|
|
1550
|
+
reactivatedAt?: Date | null;
|
|
1551
|
+
createdAt: Date;
|
|
1552
|
+
updatedAt: Date;
|
|
1553
|
+
}
|
|
1554
|
+
interface RemovedGameTimebackIntegration {
|
|
1555
|
+
integration: GameTimebackIntegration;
|
|
1556
|
+
title: string;
|
|
1557
|
+
courseCode: string;
|
|
1558
|
+
subject: TimebackSubject;
|
|
1559
|
+
grade: number;
|
|
1560
|
+
totalXp?: number | null;
|
|
1561
|
+
masterableUnits?: number | null;
|
|
1562
|
+
removedAt?: Date | string | null;
|
|
1563
|
+
metadata?: CourseMetadata | null;
|
|
1564
|
+
}
|
|
1565
|
+
interface GameTimebackIntegrationConfig {
|
|
1566
|
+
integration: GameTimebackIntegration;
|
|
1567
|
+
title: string;
|
|
1568
|
+
courseCode: string;
|
|
1569
|
+
subject: TimebackSubject;
|
|
1570
|
+
totalXp?: number | null;
|
|
1571
|
+
masterableUnits?: number | null;
|
|
1572
|
+
metadata?: CourseMetadata | null;
|
|
1573
|
+
}
|
|
1574
|
+
interface UpdateGameTimebackIntegrationRequest {
|
|
1575
|
+
title?: string;
|
|
1576
|
+
courseCode?: string;
|
|
1577
|
+
subject?: TimebackSubject;
|
|
1578
|
+
totalXp?: number | null;
|
|
1579
|
+
masterableUnits?: number | null;
|
|
1580
|
+
goals?: Partial<Record<keyof CourseGoals, number | null>> | null;
|
|
1581
|
+
publishStatus?: string | null;
|
|
1582
|
+
isSupplemental?: boolean;
|
|
1583
|
+
timebackVisible?: boolean | null;
|
|
1584
|
+
}
|
|
1585
|
+
interface CreateGameTimebackIntegrationRequest {
|
|
1586
|
+
title: string;
|
|
1587
|
+
courseCode: string;
|
|
1588
|
+
subject: TimebackSubject;
|
|
1589
|
+
grade: TimebackGrade;
|
|
1590
|
+
totalXp: number;
|
|
1591
|
+
masterableUnits: number;
|
|
1592
|
+
level?: string;
|
|
1593
|
+
}
|
|
1594
|
+
interface TimebackVerificationResources {
|
|
1595
|
+
course: {
|
|
1596
|
+
found: boolean;
|
|
1597
|
+
data?: unknown;
|
|
1598
|
+
};
|
|
1599
|
+
component: {
|
|
1600
|
+
found: boolean;
|
|
1601
|
+
data?: unknown;
|
|
1602
|
+
};
|
|
1603
|
+
resource: {
|
|
1604
|
+
found: boolean;
|
|
1605
|
+
data?: unknown;
|
|
1606
|
+
};
|
|
1607
|
+
componentResource: {
|
|
1608
|
+
found: boolean;
|
|
1609
|
+
data?: unknown;
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
interface TimebackVerifyCourseResult {
|
|
1613
|
+
integration: GameTimebackIntegration;
|
|
1614
|
+
resources: TimebackVerificationResources;
|
|
1615
|
+
status: 'success' | 'error';
|
|
1616
|
+
errors?: string[];
|
|
1617
|
+
}
|
|
1618
|
+
interface TimebackVerifyAllResponse {
|
|
1619
|
+
status: 'success' | 'error';
|
|
1620
|
+
results: TimebackVerifyCourseResult[];
|
|
1621
|
+
}
|
|
1622
|
+
interface HeartbeatRequestBase {
|
|
1623
|
+
gameId: string;
|
|
1624
|
+
studentId: string;
|
|
1625
|
+
runId: string;
|
|
1626
|
+
/**
|
|
1627
|
+
* Per-session identifier.
|
|
1628
|
+
*
|
|
1629
|
+
* LEGACY SDK COMPATIBILITY (PR #711): optional because games deployed
|
|
1630
|
+
* against the pre-resumable-sessions SDK do not send one. The
|
|
1631
|
+
* platform falls back to `runId` when absent. Make this required
|
|
1632
|
+
* once legacy SDK support is dropped.
|
|
1633
|
+
*/
|
|
1634
|
+
resumeId?: string;
|
|
1635
|
+
activityData: {
|
|
1636
|
+
activityId: string;
|
|
1637
|
+
activityName?: string;
|
|
1638
|
+
grade: TimebackGrade;
|
|
1639
|
+
subject: TimebackSubject;
|
|
1640
|
+
appName?: string;
|
|
1641
|
+
sensorUrl?: string;
|
|
1642
|
+
courseId?: string;
|
|
1643
|
+
courseName?: string;
|
|
1644
|
+
studentEmail?: string;
|
|
1645
|
+
};
|
|
1646
|
+
timingData: {
|
|
1647
|
+
activeMs: number;
|
|
1648
|
+
pausedMs: number;
|
|
1649
|
+
};
|
|
1650
|
+
isFinal?: boolean;
|
|
1651
|
+
}
|
|
1652
|
+
type HeartbeatWindowKey = {
|
|
1653
|
+
/**
|
|
1654
|
+
* Absolute start-of-window timestamp (epoch ms). Preferred dedupe key
|
|
1655
|
+
* for the post-#711 SDK.
|
|
1656
|
+
*
|
|
1657
|
+
* LEGACY SDK COMPATIBILITY (PR #711): callers must provide exactly
|
|
1658
|
+
* one of this field or `windowSequence`. Make this the only allowed
|
|
1659
|
+
* field once legacy SDK support is dropped.
|
|
1660
|
+
*/
|
|
1661
|
+
windowStartedAtMs: number;
|
|
1662
|
+
windowSequence?: never;
|
|
1663
|
+
} | {
|
|
1664
|
+
windowStartedAtMs?: never;
|
|
1665
|
+
/**
|
|
1666
|
+
* LEGACY SDK COMPATIBILITY (PR #711).
|
|
1667
|
+
*
|
|
1668
|
+
* Monotonic window counter emitted by the pre-resumable-sessions
|
|
1669
|
+
* SDK. Remove this field once every deployed game has redeployed
|
|
1670
|
+
* against the post-#711 SDK + edge-play runtime.
|
|
1671
|
+
*/
|
|
1672
|
+
windowSequence: number;
|
|
1673
|
+
};
|
|
1674
|
+
type HeartbeatRequest = HeartbeatRequestBase & HeartbeatWindowKey;
|
|
1675
|
+
interface EndActivityRequest {
|
|
1676
|
+
gameId: string;
|
|
1677
|
+
studentId: string;
|
|
1678
|
+
runId?: string;
|
|
1679
|
+
/**
|
|
1680
|
+
* Per-session identifier.
|
|
1681
|
+
*
|
|
1682
|
+
* LEGACY SDK COMPATIBILITY (PR #711): optional because games deployed
|
|
1683
|
+
* against the pre-resumable-sessions SDK do not send one. The
|
|
1684
|
+
* platform falls back to `runId`, or mints a server-side UUID, when
|
|
1685
|
+
* absent. Make this required once legacy SDK support is dropped.
|
|
1686
|
+
*/
|
|
1687
|
+
resumeId?: string;
|
|
1688
|
+
activityData: {
|
|
1689
|
+
activityId: string;
|
|
1690
|
+
activityName?: string;
|
|
1691
|
+
grade: TimebackGrade;
|
|
1692
|
+
subject: TimebackSubject;
|
|
1693
|
+
appName?: string;
|
|
1694
|
+
sensorUrl?: string;
|
|
1695
|
+
courseId?: string;
|
|
1696
|
+
courseName?: string;
|
|
1697
|
+
studentEmail?: string;
|
|
1698
|
+
};
|
|
1699
|
+
scoreData: {
|
|
1700
|
+
correctQuestions: number;
|
|
1701
|
+
totalQuestions: number;
|
|
1702
|
+
};
|
|
1703
|
+
timingData: {
|
|
1704
|
+
durationSeconds: number;
|
|
1705
|
+
};
|
|
1706
|
+
sessionTimingData?: {
|
|
1707
|
+
activeSeconds: number;
|
|
1708
|
+
inactiveSeconds?: number;
|
|
1709
|
+
};
|
|
1710
|
+
xpEarned: number;
|
|
1711
|
+
masteredUnits?: number;
|
|
1712
|
+
masteredUnitsAbsolute?: number;
|
|
1713
|
+
extensions?: TimebackActivityExtensions;
|
|
1714
|
+
}
|
|
1715
|
+
interface EndActivityResponse {
|
|
1716
|
+
status: 'ok';
|
|
1717
|
+
courseId: string;
|
|
1718
|
+
xpAwarded: number;
|
|
1719
|
+
masteredUnits?: number;
|
|
1720
|
+
pctCompleteApp?: number;
|
|
1721
|
+
warnings?: MasteryWriteWarning[];
|
|
1722
|
+
/**
|
|
1723
|
+
* Set when this submission was stopped because a completion for the same
|
|
1724
|
+
* run (`runId`) and activity had already been recorded — the platform's
|
|
1725
|
+
* idempotency guard blocked a duplicate emit (PLA-142). This call awarded
|
|
1726
|
+
* nothing new, but the award may well have happened: if the original
|
|
1727
|
+
* response was lost in transit (e.g. a gateway timeout followed by an SDK
|
|
1728
|
+
* retry), this is the only response the caller ever sees for a run that
|
|
1729
|
+
* fully succeeded. The response therefore echoes what the run earned —
|
|
1730
|
+
* `xpAwarded`, `masteredUnits`, and `pctCompleteApp` are the values
|
|
1731
|
+
* recorded when the completion was confirmed (zeros, with no
|
|
1732
|
+
* `pctCompleteApp`, only for runs recorded before award echoing existed).
|
|
1733
|
+
* Treat it as "already recorded — here's what it earned", and do not
|
|
1734
|
+
* count the run again.
|
|
1735
|
+
*/
|
|
1736
|
+
blockedByDedupeGuard?: boolean;
|
|
1737
|
+
}
|
|
1738
|
+
interface StudentHighestGradeMasteredResponse {
|
|
1739
|
+
/** Subject used for the highest-grade-mastered lookup */
|
|
1740
|
+
subject: TimebackSubject;
|
|
1741
|
+
/** EduBridge highestGradeOverall normalized to Playcademy's numeric grade type */
|
|
1742
|
+
highestGradeMastered: TimebackGrade | null;
|
|
1743
|
+
/** Normalized source grades returned by EduBridge for the rollup. */
|
|
1744
|
+
grades: {
|
|
1745
|
+
ritGrade: TimebackGrade | null;
|
|
1746
|
+
edulasticGrade: TimebackGrade | null;
|
|
1747
|
+
placementGrade: TimebackGrade | null;
|
|
1748
|
+
testOutGrade: TimebackGrade | null;
|
|
1749
|
+
highestGradeOverall: TimebackGrade | null;
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Highest-ranking OneRoster account role for a user shown in the admin
|
|
1754
|
+
* dashboard. Precedence: administrator > teacher > student. Accounts whose
|
|
1755
|
+
* roles fall outside those three (aide, guardian, …) resolve to `null`.
|
|
1756
|
+
*/
|
|
1757
|
+
type TimebackAccountRole = 'administrator' | 'teacher' | 'student';
|
|
1758
|
+
interface TimebackRosterStudent {
|
|
1759
|
+
studentId: string;
|
|
1760
|
+
enrollmentId: string | null;
|
|
1761
|
+
classId: string | null;
|
|
1762
|
+
className: string | null;
|
|
1763
|
+
name: string;
|
|
1764
|
+
email: string | null;
|
|
1765
|
+
primaryRole: TimebackAccountRole | null;
|
|
1766
|
+
analyticsUnavailable: boolean;
|
|
1767
|
+
totalXp: number;
|
|
1768
|
+
todayXp: number;
|
|
1769
|
+
activeTimeSeconds: number;
|
|
1770
|
+
masteredUnits: number;
|
|
1771
|
+
masterableUnits?: number;
|
|
1772
|
+
pctCompleteApp?: number;
|
|
1773
|
+
inactive?: boolean;
|
|
1774
|
+
}
|
|
1775
|
+
interface TimebackRosterResponse {
|
|
1776
|
+
gameId: string;
|
|
1777
|
+
courseId: string;
|
|
1778
|
+
students: TimebackRosterStudent[];
|
|
1779
|
+
}
|
|
1780
|
+
interface TimebackStudentHistoryPoint {
|
|
1781
|
+
date: string;
|
|
1782
|
+
xpEarned: number;
|
|
1783
|
+
activeTimeSeconds: number;
|
|
1784
|
+
masteredUnits: number;
|
|
1785
|
+
}
|
|
1786
|
+
type CourseCompletionStatus = 'none' | 'complete' | 'incomplete';
|
|
1787
|
+
interface TimebackStudentEnrollmentSummary {
|
|
1788
|
+
enrollmentId: string;
|
|
1789
|
+
status: 'active' | 'tobedeleted';
|
|
1790
|
+
beginDate: string | null;
|
|
1791
|
+
endDate: string | null;
|
|
1792
|
+
analyticsUnavailable: boolean;
|
|
1793
|
+
totalXp: number;
|
|
1794
|
+
todayXp: number;
|
|
1795
|
+
activeTimeSeconds: number;
|
|
1796
|
+
masteredUnits: number;
|
|
1797
|
+
pctCompleteApp?: number;
|
|
1798
|
+
history: TimebackStudentHistoryPoint[];
|
|
1799
|
+
}
|
|
1800
|
+
interface TimebackStudentCourseOverview {
|
|
1801
|
+
courseId: string;
|
|
1802
|
+
title: string;
|
|
1803
|
+
grade: number;
|
|
1804
|
+
subject: string;
|
|
1805
|
+
enrollmentId: string | null;
|
|
1806
|
+
analyticsUnavailable: boolean;
|
|
1807
|
+
totalXp: number;
|
|
1808
|
+
todayXp: number;
|
|
1809
|
+
activeTimeSeconds: number;
|
|
1810
|
+
masteredUnits: number;
|
|
1811
|
+
masterableUnits?: number;
|
|
1812
|
+
pctCompleteApp?: number;
|
|
1813
|
+
completionStatus: CourseCompletionStatus;
|
|
1814
|
+
history: TimebackStudentHistoryPoint[];
|
|
1815
|
+
inactive?: boolean;
|
|
1816
|
+
/** All enrollment records for this course, sorted active-first then by most recent. */
|
|
1817
|
+
enrollments?: TimebackStudentEnrollmentSummary[];
|
|
1818
|
+
}
|
|
1819
|
+
type TimebackRecentActivityKind = 'activity' | 'activity-in-progress' | 'time-spent' | 'remediation-xp' | 'remediation-time' | 'remediation-mastery' | 'course-completed' | 'course-resumed';
|
|
1820
|
+
interface TimebackRecentActivity {
|
|
1821
|
+
id: string;
|
|
1822
|
+
kind: TimebackRecentActivityKind;
|
|
1823
|
+
occurredAt: string;
|
|
1824
|
+
courseId: string;
|
|
1825
|
+
title: string;
|
|
1826
|
+
activityId?: string;
|
|
1827
|
+
appName?: string;
|
|
1828
|
+
reason?: string;
|
|
1829
|
+
score?: number;
|
|
1830
|
+
xpDelta?: number;
|
|
1831
|
+
timeDeltaSeconds?: number;
|
|
1832
|
+
masteredUnitsDelta?: number;
|
|
1833
|
+
runId?: string;
|
|
1834
|
+
sessionCount?: number;
|
|
1835
|
+
gameMetricsComparison?: GameRunMetricsComparisonSummary;
|
|
1836
|
+
gameMetricsVerification?: TimebackMetricDiscrepancyVerification;
|
|
1837
|
+
}
|
|
1838
|
+
interface TimebackStudentOverviewResponse {
|
|
1839
|
+
student: {
|
|
1840
|
+
studentId: string;
|
|
1841
|
+
name: string;
|
|
1842
|
+
email: string | null;
|
|
1843
|
+
};
|
|
1844
|
+
courses: TimebackStudentCourseOverview[];
|
|
1845
|
+
}
|
|
1846
|
+
interface TimebackStudentActivityResponse {
|
|
1847
|
+
activities: TimebackRecentActivity[];
|
|
1848
|
+
hasMore: boolean;
|
|
1849
|
+
}
|
|
1850
|
+
interface TimebackActivityDetailResponse {
|
|
1851
|
+
activity: TimebackRecentActivity;
|
|
1852
|
+
rawEvents: TimebackStoredCaliperEvent[];
|
|
1853
|
+
gameMetricsComparison?: GameRunMetricsComparison;
|
|
1854
|
+
}
|
|
1855
|
+
interface TimebackGradeLevelTestStudent {
|
|
1856
|
+
studentId: string;
|
|
1857
|
+
name: string;
|
|
1858
|
+
email: string | null;
|
|
1859
|
+
}
|
|
1860
|
+
interface TimebackGradeLevelTestStandardRef {
|
|
1861
|
+
source: string;
|
|
1862
|
+
id: string;
|
|
1863
|
+
identifier?: string;
|
|
1864
|
+
name?: string;
|
|
1865
|
+
description?: string;
|
|
1866
|
+
domain?: string;
|
|
1867
|
+
}
|
|
1868
|
+
interface TimebackGradeLevelTestStandardSummary extends TimebackGradeLevelTestStandardRef {
|
|
1869
|
+
attempted: number;
|
|
1870
|
+
missed: number;
|
|
1871
|
+
}
|
|
1872
|
+
interface TimebackGradeLevelTestSummary {
|
|
1873
|
+
resultId: string;
|
|
1874
|
+
lineItemId: string;
|
|
1875
|
+
studentId: string;
|
|
1876
|
+
testName: string;
|
|
1877
|
+
testType: string;
|
|
1878
|
+
provider: string;
|
|
1879
|
+
subject: string;
|
|
1880
|
+
grade: number | null;
|
|
1881
|
+
score: number | null;
|
|
1882
|
+
scorePercentile: number | null;
|
|
1883
|
+
scoreStatus: string;
|
|
1884
|
+
scoreDate: string | null;
|
|
1885
|
+
assignmentId?: string;
|
|
1886
|
+
qtiTestId?: string;
|
|
1887
|
+
questionCount?: number;
|
|
1888
|
+
correctQuestionCount?: number;
|
|
1889
|
+
}
|
|
1890
|
+
type TimebackGradeLevelQuestionCorrectness = 'correct' | 'incorrect' | 'partial' | 'unknown';
|
|
1891
|
+
interface TimebackGradeLevelTestQuestionResult {
|
|
1892
|
+
lineItemId: string;
|
|
1893
|
+
resultId: string | null;
|
|
1894
|
+
title: string;
|
|
1895
|
+
description?: string;
|
|
1896
|
+
correctness: TimebackGradeLevelQuestionCorrectness;
|
|
1897
|
+
score: number | null;
|
|
1898
|
+
scorePercentile: number | null;
|
|
1899
|
+
scoreStatus: string | null;
|
|
1900
|
+
standards: TimebackGradeLevelTestStandardRef[];
|
|
1901
|
+
qtiQuestion?: QtiTestQuestionRef | null;
|
|
1902
|
+
feedback?: string | null;
|
|
1903
|
+
}
|
|
1904
|
+
interface TimebackGradeLevelTestResultsResponse {
|
|
1905
|
+
gameId: string;
|
|
1906
|
+
courseId: string;
|
|
1907
|
+
subject: TimebackSubject;
|
|
1908
|
+
grade: TimebackGrade;
|
|
1909
|
+
student: TimebackGradeLevelTestStudent;
|
|
1910
|
+
highestGradeMastered: StudentHighestGradeMasteredResponse | null;
|
|
1911
|
+
results: TimebackGradeLevelTestSummary[];
|
|
1912
|
+
}
|
|
1913
|
+
interface TimebackGradeLevelTestReviewResponse {
|
|
1914
|
+
parentResultId: string;
|
|
1915
|
+
parentLineItemId: string;
|
|
1916
|
+
studentId: string;
|
|
1917
|
+
qtiQuestionCount: number;
|
|
1918
|
+
questions: TimebackGradeLevelTestQuestionResult[];
|
|
1919
|
+
standards: TimebackGradeLevelTestStandardSummary[];
|
|
1920
|
+
}
|
|
1921
|
+
type TimebackDiscrepancyQueueWindow = 'all' | 'today' | 'yesterday' | 'this-week' | 'last-week' | 'custom';
|
|
1922
|
+
interface TimebackDiscrepancyQueueDateRange {
|
|
1923
|
+
startDate?: string;
|
|
1924
|
+
endDate?: string;
|
|
1925
|
+
}
|
|
1926
|
+
interface TimebackMetricDiscrepancyVerification {
|
|
1927
|
+
gameId: string;
|
|
1928
|
+
courseId: string;
|
|
1929
|
+
studentId: string;
|
|
1930
|
+
runId: string;
|
|
1931
|
+
activityId?: string | null;
|
|
1932
|
+
verifiedAt: string;
|
|
1933
|
+
verifiedByUserId?: string | null;
|
|
1934
|
+
}
|
|
1935
|
+
interface TimebackDiscrepancyQueueStudent {
|
|
1936
|
+
studentId: string;
|
|
1937
|
+
name: string;
|
|
1938
|
+
email: string | null;
|
|
1939
|
+
}
|
|
1940
|
+
interface TimebackDiscrepancyQueueEventPage {
|
|
1941
|
+
offset: number;
|
|
1942
|
+
limit: number;
|
|
1943
|
+
total: number;
|
|
1944
|
+
hasPrevious: boolean;
|
|
1945
|
+
hasNext: boolean;
|
|
1946
|
+
}
|
|
1947
|
+
interface TimebackMetricDiscrepancyQueueItem {
|
|
1948
|
+
student: TimebackDiscrepancyQueueStudent;
|
|
1949
|
+
activity: TimebackRecentActivity;
|
|
1950
|
+
gameMetricsComparison: GameRunMetricsComparison;
|
|
1951
|
+
verification?: TimebackMetricDiscrepancyVerification;
|
|
1952
|
+
comparisonSource?: 'hydrated' | 'preliminary';
|
|
1953
|
+
}
|
|
1954
|
+
interface TimebackMetricDiscrepancyQueueResponse {
|
|
1955
|
+
gameId: string;
|
|
1956
|
+
courseId: string;
|
|
1957
|
+
window: TimebackDiscrepancyQueueWindow;
|
|
1958
|
+
dateRange: TimebackDiscrepancyQueueDateRange;
|
|
1959
|
+
studentId?: string;
|
|
1960
|
+
students: TimebackDiscrepancyQueueStudent[];
|
|
1961
|
+
discrepancyMetricScopes: GameMetricComparisonMetric[];
|
|
1962
|
+
eventPage: TimebackDiscrepancyQueueEventPage;
|
|
1963
|
+
includeVerified: boolean;
|
|
1964
|
+
items: TimebackMetricDiscrepancyQueueItem[];
|
|
1965
|
+
}
|
|
1966
|
+
interface VerifyTimebackMetricDiscrepancyRequest {
|
|
1967
|
+
studentId: string;
|
|
1968
|
+
runId: string;
|
|
1969
|
+
activityId?: string;
|
|
1970
|
+
}
|
|
1971
|
+
interface VerifyTimebackMetricDiscrepancyResponse {
|
|
1972
|
+
status: 'ok';
|
|
1973
|
+
verification: TimebackMetricDiscrepancyVerification;
|
|
1974
|
+
}
|
|
1975
|
+
interface GrantTimebackXpRequest {
|
|
1976
|
+
gameId: string;
|
|
1977
|
+
courseId: string;
|
|
1978
|
+
studentId: string;
|
|
1979
|
+
xp: number;
|
|
1980
|
+
reason: string;
|
|
1981
|
+
date?: string;
|
|
1982
|
+
useCurrentTime?: boolean;
|
|
1983
|
+
}
|
|
1984
|
+
interface AdjustTimebackTimeRequest {
|
|
1985
|
+
gameId: string;
|
|
1986
|
+
courseId: string;
|
|
1987
|
+
studentId: string;
|
|
1988
|
+
seconds: number;
|
|
1989
|
+
reason: string;
|
|
1990
|
+
date?: string;
|
|
1991
|
+
/** See {@link GrantTimebackXpRequest.useCurrentTime}. */
|
|
1992
|
+
useCurrentTime?: boolean;
|
|
1993
|
+
}
|
|
1994
|
+
interface AdjustTimebackMasteryRequest {
|
|
1995
|
+
gameId: string;
|
|
1996
|
+
courseId: string;
|
|
1997
|
+
studentId: string;
|
|
1998
|
+
units: number;
|
|
1999
|
+
reason: string;
|
|
2000
|
+
date?: string;
|
|
2001
|
+
/** See {@link GrantTimebackXpRequest.useCurrentTime}. */
|
|
2002
|
+
useCurrentTime?: boolean;
|
|
2003
|
+
}
|
|
2004
|
+
interface EnrollStudentRequest {
|
|
2005
|
+
gameId: string;
|
|
2006
|
+
courseId: string;
|
|
2007
|
+
studentIds: string[];
|
|
2008
|
+
}
|
|
2009
|
+
interface EnrollStudentResult {
|
|
2010
|
+
studentId: string;
|
|
2011
|
+
status: 'ok' | 'already-enrolled' | 'not-found' | 'error';
|
|
2012
|
+
error?: string;
|
|
2013
|
+
}
|
|
2014
|
+
interface EnrollStudentResponse {
|
|
2015
|
+
results: EnrollStudentResult[];
|
|
2016
|
+
}
|
|
2017
|
+
interface ReactivateEnrollmentRequest {
|
|
2018
|
+
gameId: string;
|
|
2019
|
+
courseId: string;
|
|
2020
|
+
studentId: string;
|
|
2021
|
+
enrollmentId: string;
|
|
2022
|
+
}
|
|
2023
|
+
interface UnenrollStudentRequest {
|
|
2024
|
+
gameId: string;
|
|
2025
|
+
courseId: string;
|
|
2026
|
+
studentId: string;
|
|
2027
|
+
}
|
|
2028
|
+
interface SearchStudentPastEnrollment {
|
|
2029
|
+
enrollmentId: string;
|
|
2030
|
+
beginDate: string | null;
|
|
2031
|
+
endDate: string | null;
|
|
2032
|
+
}
|
|
2033
|
+
interface SearchStudentResult {
|
|
2034
|
+
studentId: string;
|
|
2035
|
+
name: string;
|
|
2036
|
+
email: string | null;
|
|
2037
|
+
/** Always set: search only returns accounts holding a supported role. */
|
|
2038
|
+
primaryRole: TimebackAccountRole;
|
|
2039
|
+
alreadyEnrolled: boolean;
|
|
2040
|
+
pastEnrollments?: SearchStudentPastEnrollment[];
|
|
2041
|
+
}
|
|
2042
|
+
interface SearchStudentsResponse {
|
|
2043
|
+
students: SearchStudentResult[];
|
|
2044
|
+
}
|
|
2045
|
+
interface TimebackAdminMutationResponse {
|
|
2046
|
+
status: 'ok';
|
|
2047
|
+
}
|
|
2048
|
+
interface ReconcileMasteryForConfigChangeRequest {
|
|
2049
|
+
gameId: string;
|
|
2050
|
+
courseId: string;
|
|
2051
|
+
oldMasterableUnits: number;
|
|
2052
|
+
newMasterableUnits: number;
|
|
2053
|
+
affectedStudentIds: string[];
|
|
2054
|
+
}
|
|
2055
|
+
interface ReconcileMasteryForConfigChangeResponse {
|
|
2056
|
+
processed: number;
|
|
2057
|
+
failed: string[];
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
/** Schema version for the declarative platform-routed diagnostic manifest. */
|
|
2061
|
+
declare const DIAGNOSTIC_ROUTING_MANIFEST_VERSION: 1;
|
|
2062
|
+
declare const DIAGNOSTIC_TRACK_OUTCOMES: readonly ['provisional', 'unresolved', 'route-to-instruction', 'not-assessed'];
|
|
2063
|
+
type DiagnosticTrackOutcome = (typeof DIAGNOSTIC_TRACK_OUTCOMES)[number];
|
|
2064
|
+
interface DiagnosticRoutingGroupV1 {
|
|
2065
|
+
key: string;
|
|
2066
|
+
title?: string;
|
|
2067
|
+
}
|
|
2068
|
+
interface DiagnosticRoundRobinScheduleV1 {
|
|
2069
|
+
type: 'round-robin';
|
|
2070
|
+
}
|
|
2071
|
+
type DiagnosticActivationPredicateV1 = {
|
|
2072
|
+
type: 'node-result';
|
|
2073
|
+
nodeKey: string;
|
|
2074
|
+
isCorrect: boolean;
|
|
2075
|
+
} | {
|
|
2076
|
+
/** Gates route on graph identity, never on the game-facing semantic result. */
|
|
2077
|
+
type: 'track-terminal';
|
|
2078
|
+
trackKey: string;
|
|
2079
|
+
terminalKey: string;
|
|
2080
|
+
} | {
|
|
2081
|
+
type: 'all' | 'any';
|
|
2082
|
+
predicates: DiagnosticActivationPredicateV1[];
|
|
2083
|
+
};
|
|
2084
|
+
interface DiagnosticRoutingStageV1 {
|
|
2085
|
+
key: string;
|
|
2086
|
+
schedule: DiagnosticRoundRobinScheduleV1;
|
|
2087
|
+
trackKeys: string[];
|
|
2088
|
+
/** Gates may reference only nodes or tracks from an earlier stage. */
|
|
2089
|
+
activateWhen?: DiagnosticActivationPredicateV1;
|
|
2090
|
+
}
|
|
2091
|
+
interface DiagnosticRoutingTrackV1 {
|
|
2092
|
+
key: string;
|
|
2093
|
+
groupKey?: string;
|
|
2094
|
+
entryNodeKey: string;
|
|
2095
|
+
/** Required for a track in a gated stage; must resolve to a not-assessed terminal. */
|
|
2096
|
+
inactiveTerminalKey?: string;
|
|
2097
|
+
}
|
|
2098
|
+
type DiagnosticRoutingTransitionV1 = {
|
|
2099
|
+
type: 'goto';
|
|
2100
|
+
nodeKey: string;
|
|
2101
|
+
} | {
|
|
2102
|
+
type: 'complete';
|
|
2103
|
+
terminalKey: string;
|
|
2104
|
+
};
|
|
2105
|
+
interface DiagnosticRoutingNodeV1 {
|
|
2106
|
+
/** Routing-context identity; deliberately distinct from the QTI item identifier. */
|
|
2107
|
+
key: string;
|
|
2108
|
+
trackKey: string;
|
|
2109
|
+
itemIdentifier: string;
|
|
2110
|
+
transitions: {
|
|
2111
|
+
correct: DiagnosticRoutingTransitionV1;
|
|
2112
|
+
incorrect: DiagnosticRoutingTransitionV1;
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
interface DiagnosticRoutingTerminalV1 {
|
|
2116
|
+
/** Internal routing-graph stop identity. */
|
|
2117
|
+
key: string;
|
|
2118
|
+
trackKey: string;
|
|
2119
|
+
outcome: DiagnosticTrackOutcome;
|
|
2120
|
+
/** Stable semantic result consumed by the game after diagnostic completion. */
|
|
2121
|
+
resultKey: string;
|
|
2122
|
+
}
|
|
2123
|
+
/** Immutable sidecar attached to one exact diagnostic QTI bank revision. */
|
|
2124
|
+
interface DiagnosticRoutingManifestV1 {
|
|
2125
|
+
version: typeof DIAGNOSTIC_ROUTING_MANIFEST_VERSION;
|
|
2126
|
+
groups?: DiagnosticRoutingGroupV1[];
|
|
2127
|
+
stages: DiagnosticRoutingStageV1[];
|
|
2128
|
+
tracks: DiagnosticRoutingTrackV1[];
|
|
2129
|
+
nodes: DiagnosticRoutingNodeV1[];
|
|
2130
|
+
terminals: DiagnosticRoutingTerminalV1[];
|
|
2131
|
+
}
|
|
2132
|
+
type DiagnosticRoutingTrackStatus = 'pending' | 'active' | 'completed' | 'skipped';
|
|
2133
|
+
type DiagnosticRoutingStatus = 'in-progress' | 'ready-to-complete';
|
|
2134
|
+
interface DiagnosticRoutingNextItem {
|
|
2135
|
+
stageKey: string;
|
|
2136
|
+
trackKey: string;
|
|
2137
|
+
nodeKey: string;
|
|
2138
|
+
itemIdentifier: string;
|
|
2139
|
+
}
|
|
2140
|
+
/** Deliberately small semantic result exposed to normal game completion handlers. */
|
|
2141
|
+
interface DiagnosticTrackResult {
|
|
2142
|
+
trackKey: string;
|
|
2143
|
+
groupKey?: string;
|
|
2144
|
+
outcome: DiagnosticTrackOutcome;
|
|
2145
|
+
resultKey: string;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
/** Stable curriculum-standard identity used to align and select assessment content. */
|
|
2149
|
+
interface AssessmentStandardRef {
|
|
2150
|
+
framework: string;
|
|
2151
|
+
identifier: string;
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Playcademy's course-specific meaning for reusable QTI test content.
|
|
2155
|
+
* Result writers translate this to OneRoster assessment-result `metadata.testType`;
|
|
2156
|
+
* it intentionally does not become metadata on the QTI test itself.
|
|
2157
|
+
*/
|
|
2158
|
+
type AssessmentPurpose = (typeof ASSESSMENT_PURPOSES)[number];
|
|
2159
|
+
type AssessmentStatus = 'draft' | 'live' | 'archived';
|
|
2160
|
+
/** Immutable routing definition attached to one diagnostic assessment association. */
|
|
2161
|
+
interface DiagnosticAssessmentDefinition {
|
|
2162
|
+
diagnosticKey: string;
|
|
2163
|
+
routingManifest: DiagnosticRoutingManifestV1;
|
|
2164
|
+
}
|
|
2165
|
+
/** Caller-authored diagnostic sidecar. */
|
|
2166
|
+
type DiagnosticAssessmentDefinitionInput = DiagnosticAssessmentDefinition;
|
|
2167
|
+
interface AssessmentSummary {
|
|
2168
|
+
id: string;
|
|
2169
|
+
integrationId: string;
|
|
2170
|
+
/** Stable logical identity. Null for unmanaged, read-only QTI drafts. */
|
|
2171
|
+
assessmentKey: string | null;
|
|
2172
|
+
qtiTestIdentifier: string;
|
|
2173
|
+
purpose: AssessmentPurpose;
|
|
2174
|
+
status: AssessmentStatus;
|
|
2175
|
+
standard: AssessmentStandardRef | null;
|
|
2176
|
+
/** Null for non-diagnostics and historical fixed diagnostic associations. */
|
|
2177
|
+
diagnostic: DiagnosticAssessmentDefinition | null;
|
|
2178
|
+
createdAt: string;
|
|
2179
|
+
updatedAt: string;
|
|
2180
|
+
title: string;
|
|
2181
|
+
questionCount: number;
|
|
2182
|
+
available: boolean;
|
|
2183
|
+
editable: boolean;
|
|
2184
|
+
}
|
|
2185
|
+
interface AssessmentRow {
|
|
2186
|
+
id: string;
|
|
2187
|
+
integrationId: string;
|
|
2188
|
+
/** Stable logical identity. Null for unmanaged, read-only QTI drafts. */
|
|
2189
|
+
assessmentKey: string | null;
|
|
2190
|
+
qtiTestIdentifier: string;
|
|
2191
|
+
purpose: AssessmentPurpose;
|
|
2192
|
+
status: AssessmentStatus;
|
|
2193
|
+
standard: AssessmentStandardRef | null;
|
|
2194
|
+
/** Null for non-diagnostics and historical fixed diagnostic associations. */
|
|
2195
|
+
diagnostic: DiagnosticAssessmentDefinition | null;
|
|
2196
|
+
createdAt: string;
|
|
2197
|
+
updatedAt: string;
|
|
2198
|
+
/** Non-fatal warning produced by the requested assessment operation. */
|
|
2199
|
+
warning?: string;
|
|
2200
|
+
}
|
|
2201
|
+
/** Request body for replacing a live assessment's content in place. */
|
|
2202
|
+
interface AssessmentRevisionRequest {
|
|
2203
|
+
/**
|
|
2204
|
+
* The publication the author last saw. Doubles as an optimistic-concurrency token: if the
|
|
2205
|
+
* assessment was revised in the meantime the request fails with a conflict.
|
|
2206
|
+
*/
|
|
2207
|
+
currentQtiTestIdentifier: string;
|
|
2208
|
+
title: string;
|
|
2209
|
+
/** Structured question payloads, in order, as accepted by question creation. */
|
|
2210
|
+
questions: Record<string, unknown>[];
|
|
2211
|
+
}
|
|
2212
|
+
/** Result of revising a live assessment. `qtiTestIdentifier` is the publication now live. */
|
|
2213
|
+
interface AssessmentRevisionResult extends AssessmentRow {
|
|
2214
|
+
/** The publication that was live before this call; equals `qtiTestIdentifier` when unchanged. */
|
|
2215
|
+
previousQtiTestIdentifier: string;
|
|
2216
|
+
/** True when the new content hashed to the current publication and nothing changed. */
|
|
2217
|
+
unchanged: boolean;
|
|
2218
|
+
}
|
|
2219
|
+
/** One shared-QTI test reference carried by an association promotion manifest. */
|
|
2220
|
+
interface AssessmentAssociationImportEntry {
|
|
2221
|
+
/** Stable logical identity used to attach this publication to the target course. */
|
|
2222
|
+
assessmentKey: string;
|
|
2223
|
+
qtiTestIdentifier: string;
|
|
2224
|
+
purpose: AssessmentPurpose;
|
|
2225
|
+
standard?: AssessmentStandardRef;
|
|
2226
|
+
}
|
|
2227
|
+
/** Portable Playcademy association metadata derived from an assessment fixture export. */
|
|
2228
|
+
interface AssessmentAssociationImportManifest {
|
|
2229
|
+
version: 1;
|
|
2230
|
+
sourceGameId: string;
|
|
2231
|
+
sourceGameSlug?: string;
|
|
2232
|
+
sourceIntegrationId: string;
|
|
2233
|
+
subject: TimebackSubject;
|
|
2234
|
+
grade: TimebackGrade;
|
|
2235
|
+
/** Lifecycle state assigned to newly attached target associations. */
|
|
2236
|
+
targetStatus: Extract<AssessmentStatus, 'draft' | 'live'>;
|
|
2237
|
+
assessments: AssessmentAssociationImportEntry[];
|
|
2238
|
+
}
|
|
2239
|
+
type AssessmentAssociationImportResultStatus = 'created' | 'skipped' | 'failed';
|
|
2240
|
+
/** Per-test outcome from attaching existing shared QTI content to another course. */
|
|
2241
|
+
interface AssessmentAssociationImportResult {
|
|
2242
|
+
assessmentKey: string;
|
|
2243
|
+
qtiTestIdentifier: string;
|
|
2244
|
+
title?: string;
|
|
2245
|
+
purpose: AssessmentPurpose;
|
|
2246
|
+
standard?: AssessmentStandardRef;
|
|
2247
|
+
status: AssessmentAssociationImportResultStatus;
|
|
2248
|
+
association?: AssessmentRow;
|
|
2249
|
+
editable?: boolean;
|
|
2250
|
+
message?: string;
|
|
2251
|
+
}
|
|
2252
|
+
interface AssessmentAssociationImportResponse {
|
|
2253
|
+
results: AssessmentAssociationImportResult[];
|
|
2254
|
+
}
|
|
2255
|
+
/** One shared item was claimed by multiple mastery standards; only the first assignment stands. */
|
|
2256
|
+
interface ReviewBankSyncWarning {
|
|
2257
|
+
code: 'QUESTION_STANDARD_CONFLICT';
|
|
2258
|
+
itemIdentifier: string;
|
|
2259
|
+
retainedStandard: AssessmentStandardRef;
|
|
2260
|
+
skippedStandard: AssessmentStandardRef;
|
|
2261
|
+
skippedQtiTestIdentifier: string;
|
|
2262
|
+
message: string;
|
|
2263
|
+
}
|
|
2264
|
+
/** Optimized artifacts could not be prepared, so QTI remains the runtime fallback. */
|
|
2265
|
+
interface AssessmentArtifactsUnavailableWarning {
|
|
2266
|
+
code: 'ASSESSMENT_ARTIFACTS_UNAVAILABLE';
|
|
2267
|
+
message: string;
|
|
2268
|
+
}
|
|
2269
|
+
type ReviewBankSynchronizationWarning = ReviewBankSyncWarning | AssessmentArtifactsUnavailableWarning;
|
|
2270
|
+
/** Wire result of synchronizing the review-bank sink to the live mastery catalog. */
|
|
2271
|
+
interface ReviewBankSynchronizationResult {
|
|
2272
|
+
qtiTestIdentifier: string;
|
|
2273
|
+
itemCount: number;
|
|
2274
|
+
standardCount: number;
|
|
2275
|
+
contributorCount: number;
|
|
2276
|
+
sourceFingerprint: string;
|
|
2277
|
+
addedItemCount: number;
|
|
2278
|
+
removedItemCount: number;
|
|
2279
|
+
unchanged: boolean;
|
|
2280
|
+
warnings: readonly ReviewBankSynchronizationWarning[];
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
/** Private, declarative scoring content compiled from one immutable QTI publication. */
|
|
2284
|
+
/** QTI response shapes the platform grader can compare without a QTI engine. */
|
|
2285
|
+
type AssessmentScoringBaseTypeV1 = 'string' | 'identifier' | 'integer' | 'float' | 'directedPair';
|
|
2286
|
+
type AssessmentScoringCardinalityV1 = 'single' | 'multiple' | 'ordered';
|
|
2287
|
+
/** Exact QTI match semantics, interpreted according to `baseType` and `cardinality`. */
|
|
2288
|
+
interface AssessmentScoringMatchComparisonV1 {
|
|
2289
|
+
kind: 'match';
|
|
2290
|
+
/** Single string responses may list TimeBack-compatible accepted alternatives. */
|
|
2291
|
+
correctValues: string[];
|
|
2292
|
+
}
|
|
2293
|
+
/** Numeric equality after parsing both operands as the rule's declared numeric base type. */
|
|
2294
|
+
interface AssessmentScoringNumericEqualComparisonV1 {
|
|
2295
|
+
kind: 'numeric-equal';
|
|
2296
|
+
correctValue: number;
|
|
2297
|
+
}
|
|
2298
|
+
/** Numeric equality after applying the QTI equal-rounded comparison to both operands. */
|
|
2299
|
+
interface AssessmentScoringNumericEqualRoundedComparisonV1 {
|
|
2300
|
+
kind: 'numeric-equal-rounded';
|
|
2301
|
+
correctValue: number;
|
|
2302
|
+
roundingMode: 'decimalPlaces' | 'significantFigures';
|
|
2303
|
+
figures: number;
|
|
2304
|
+
}
|
|
2305
|
+
type AssessmentScoringComparisonV1 = AssessmentScoringMatchComparisonV1 | AssessmentScoringNumericEqualComparisonV1 | AssessmentScoringNumericEqualRoundedComparisonV1;
|
|
2306
|
+
/** One independently scored response. Correct-answer material never enters the playable artifact. */
|
|
2307
|
+
interface AssessmentScoringRuleV1 {
|
|
2308
|
+
responseIdentifier: string;
|
|
2309
|
+
cardinality: AssessmentScoringCardinalityV1;
|
|
2310
|
+
baseType: AssessmentScoringBaseTypeV1;
|
|
2311
|
+
points: number;
|
|
2312
|
+
comparison: AssessmentScoringComparisonV1;
|
|
2313
|
+
}
|
|
2314
|
+
/** Stable reason codes for routing an item back through authoritative QTI grading. */
|
|
2315
|
+
type AssessmentQtiScoringFallbackReasonV1 = 'response-mapping' | 'area-mapping' | 'unsupported-base-type' | 'unsupported-cardinality' | 'invalid-correct-response' | 'no-scorable-response' | 'unsupported-processing-template' | 'unsupported-response-processing' | 'cross-response-processing' | 'inconsistent-max-score';
|
|
2316
|
+
interface PlatformAssessmentScoringArtifactItemV1 {
|
|
2317
|
+
strategy: 'platform';
|
|
2318
|
+
itemIdentifier: string;
|
|
2319
|
+
maxScore: number;
|
|
2320
|
+
rules: AssessmentScoringRuleV1[];
|
|
2321
|
+
}
|
|
2322
|
+
/** No answers are retained for a fallback item; the QTI source remains authoritative. */
|
|
2323
|
+
interface QtiAssessmentScoringArtifactItemV1 {
|
|
2324
|
+
strategy: 'qti';
|
|
2325
|
+
itemIdentifier: string;
|
|
2326
|
+
maxScore: number;
|
|
2327
|
+
reason: AssessmentQtiScoringFallbackReasonV1;
|
|
2328
|
+
}
|
|
2329
|
+
type AssessmentScoringArtifactItemV1 = PlatformAssessmentScoringArtifactItemV1 | QtiAssessmentScoringArtifactItemV1;
|
|
2330
|
+
/** Private grader payload assembled in deterministic QTI question order. */
|
|
2331
|
+
interface AssessmentScoringArtifactV1 {
|
|
2332
|
+
items: AssessmentScoringArtifactItemV1[];
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
/** One answer value accepted by the supported QTI runtime interactions. */
|
|
2336
|
+
type AssessmentResponseValue = string | string[];
|
|
2337
|
+
/** Canonical response state persisted on an assessment attempt. */
|
|
2338
|
+
type AssessmentResponses = Record<string, Record<string, AssessmentResponseValue>>;
|
|
2339
|
+
/**
|
|
2340
|
+
* Partial update accepted by save(), merged into the stored responses at both
|
|
2341
|
+
* levels: an omitted question keeps its answers, and an omitted response keeps
|
|
2342
|
+
* its value. Null clears one response, and clearing the last response of a
|
|
2343
|
+
* question drops the question.
|
|
2344
|
+
*
|
|
2345
|
+
* Merging rather than replacing is what makes the obvious write safe. An item
|
|
2346
|
+
* may hold several interactions, so saving the one a student just answered —
|
|
2347
|
+
* `{ item: { RESPONSE_2: 'B' } }` — must not disturb its siblings. Under
|
|
2348
|
+
* replacement it silently discarded them, and nothing in the payload said so.
|
|
2349
|
+
*/
|
|
2350
|
+
type AssessmentResponseUpdate = Record<string, Record<string, AssessmentResponseValue | null>>;
|
|
2351
|
+
/**
|
|
2352
|
+
* One node of the rich-content vocabulary, which is OPEN: kinds are added as
|
|
2353
|
+
* content that plain text would misrepresent appears, so a consumer must not
|
|
2354
|
+
* treat this union as exhaustive.
|
|
2355
|
+
*
|
|
2356
|
+
* Structured content is projected from sanitized QTI on the host — never raw
|
|
2357
|
+
* XML/HTML — and always travels alongside a flattened plain-text equivalent, so
|
|
2358
|
+
* simple clients can ignore it entirely.
|
|
2359
|
+
*
|
|
2360
|
+
* A node carries no field common to every kind, so there is nothing to
|
|
2361
|
+
* introspect on an unrecognized one. The supported fallback is one level up:
|
|
2362
|
+
* abandon the whole subtree and render the enclosing `prompt` / `content`
|
|
2363
|
+
* string, which is always present.
|
|
2364
|
+
*
|
|
2365
|
+
* Say so on screen when that happens. A question whose diagram or expression
|
|
2366
|
+
* carried what was being asked still reads as complete prose once the structure
|
|
2367
|
+
* is gone, so a silent fallback can leave a student an unanswerable question
|
|
2368
|
+
* with nothing to indicate why.
|
|
2369
|
+
*/
|
|
2370
|
+
type PlayableContentNode = {
|
|
2371
|
+
kind: 'text';
|
|
2372
|
+
text: string;
|
|
2373
|
+
} | {
|
|
2374
|
+
kind: 'math';
|
|
2375
|
+
/** Sanitized MathML source for clients that can render it. */
|
|
2376
|
+
mathMl: string;
|
|
2377
|
+
/** Plain-text approximation for clients that cannot. */
|
|
2378
|
+
fallbackText: string;
|
|
2379
|
+
} | {
|
|
2380
|
+
kind: 'image';
|
|
2381
|
+
src: string;
|
|
2382
|
+
width?: number;
|
|
2383
|
+
height?: number;
|
|
2384
|
+
description?: string;
|
|
2385
|
+
} | {
|
|
2386
|
+
kind: 'paragraph';
|
|
2387
|
+
children: PlayableContentNode[];
|
|
2388
|
+
} | {
|
|
2389
|
+
kind: 'emphasis';
|
|
2390
|
+
children: PlayableContentNode[];
|
|
2391
|
+
} | {
|
|
2392
|
+
kind: 'strong';
|
|
2393
|
+
children: PlayableContentNode[];
|
|
2394
|
+
} | {
|
|
2395
|
+
kind: 'table';
|
|
2396
|
+
rows: {
|
|
2397
|
+
header: boolean;
|
|
2398
|
+
cells: PlayableContentNode[][];
|
|
2399
|
+
}[];
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* A bulleted or numbered list. Modelled because flattening one to plain text
|
|
2403
|
+
* genuinely changes its meaning — "one two" is not a list — unlike inline
|
|
2404
|
+
* emphasis, which reads correctly either way and so is not carried.
|
|
2405
|
+
*/
|
|
2406
|
+
| {
|
|
2407
|
+
kind: 'list';
|
|
2408
|
+
/** Numbered (`ol`) rather than bulleted (`ul`). */
|
|
2409
|
+
ordered: boolean;
|
|
2410
|
+
items: PlayableContentNode[][];
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Where one inline interaction belongs in the prose, naming the interaction
|
|
2414
|
+
* it stands for rather than relying on its position among sibling blanks.
|
|
2415
|
+
*
|
|
2416
|
+
* The identifier is what makes this robust: matching blanks to interactions
|
|
2417
|
+
* by order silently mispairs them when either sequence shifts, and a
|
|
2418
|
+
* mispaired dropdown produces a wrong answer rather than a visible error.
|
|
2419
|
+
*/
|
|
2420
|
+
| {
|
|
2421
|
+
kind: 'blank';
|
|
2422
|
+
responseIdentifier: string;
|
|
2423
|
+
}
|
|
2424
|
+
/** One named target inside a gap-match passage. */
|
|
2425
|
+
| {
|
|
2426
|
+
kind: 'gap';
|
|
2427
|
+
identifier: string;
|
|
2428
|
+
};
|
|
2429
|
+
interface PlayableAssessmentChoice {
|
|
2430
|
+
identifier: string;
|
|
2431
|
+
content: string;
|
|
2432
|
+
/** Keep this choice in its authored slot when the interaction is shuffled. */
|
|
2433
|
+
fixed?: boolean;
|
|
2434
|
+
/** Rich content when the choice carries more than plain text. */
|
|
2435
|
+
contentNodes?: PlayableContentNode[];
|
|
2436
|
+
}
|
|
2437
|
+
/** A Match choice, which unlike an ordinary choice carries an association capacity. */
|
|
2438
|
+
interface PlayableMatchChoice extends PlayableAssessmentChoice {
|
|
2439
|
+
/** Associations this choice accepts (0 means unlimited). */
|
|
2440
|
+
matchMax: number;
|
|
2441
|
+
}
|
|
2442
|
+
interface PlayableAssessmentInteractionBase {
|
|
2443
|
+
responseIdentifier: string;
|
|
2444
|
+
/** Prompt declared inside the interaction (QTI qti-prompt), when present. */
|
|
2445
|
+
prompt?: string;
|
|
2446
|
+
}
|
|
2447
|
+
interface PlayableChoiceInteraction extends PlayableAssessmentInteractionBase {
|
|
2448
|
+
type: 'choice';
|
|
2449
|
+
choices: PlayableAssessmentChoice[];
|
|
2450
|
+
/** The host has already applied attempt-stable ordering; render the received array. */
|
|
2451
|
+
shuffle: boolean;
|
|
2452
|
+
minChoices: number;
|
|
2453
|
+
maxChoices: number;
|
|
2454
|
+
}
|
|
2455
|
+
interface PlayableInlineChoiceInteraction extends PlayableAssessmentInteractionBase {
|
|
2456
|
+
type: 'inline-choice';
|
|
2457
|
+
choices: PlayableAssessmentChoice[];
|
|
2458
|
+
/** The host has already applied attempt-stable ordering; render the received array. */
|
|
2459
|
+
shuffle: boolean;
|
|
2460
|
+
}
|
|
2461
|
+
interface PlayableTextEntryInteraction extends PlayableAssessmentInteractionBase {
|
|
2462
|
+
type: 'text-entry';
|
|
2463
|
+
expectedLength?: number;
|
|
2464
|
+
placeholder?: string;
|
|
2465
|
+
}
|
|
2466
|
+
interface PlayableOrderInteraction extends PlayableAssessmentInteractionBase {
|
|
2467
|
+
type: 'order';
|
|
2468
|
+
choices: PlayableAssessmentChoice[];
|
|
2469
|
+
/** Whether the host applies attempt-stable presentation ordering. */
|
|
2470
|
+
shuffle: boolean;
|
|
2471
|
+
}
|
|
2472
|
+
interface PlayableMatchInteraction extends PlayableAssessmentInteractionBase {
|
|
2473
|
+
type: 'match';
|
|
2474
|
+
sourceChoices: PlayableMatchChoice[];
|
|
2475
|
+
targetChoices: PlayableMatchChoice[];
|
|
2476
|
+
maxAssociations: number;
|
|
2477
|
+
/** Whether the host applies independent attempt-stable ordering to both choice sets. */
|
|
2478
|
+
shuffle: boolean;
|
|
2479
|
+
}
|
|
2480
|
+
interface PlayableHottextSegment {
|
|
2481
|
+
identifier?: string;
|
|
2482
|
+
content: string;
|
|
2483
|
+
selectable: boolean;
|
|
2484
|
+
}
|
|
2485
|
+
interface PlayableHottextInteraction extends PlayableAssessmentInteractionBase {
|
|
2486
|
+
type: 'hottext';
|
|
2487
|
+
segments: PlayableHottextSegment[];
|
|
2488
|
+
maxChoices: number;
|
|
2489
|
+
}
|
|
2490
|
+
/** A sanitized graphic reference rendered by graphic interactions. */
|
|
2491
|
+
interface PlayableAssessmentGraphic {
|
|
2492
|
+
src: string;
|
|
2493
|
+
width?: number;
|
|
2494
|
+
height?: number;
|
|
2495
|
+
/** Accessible description carried from the source QTI. */
|
|
2496
|
+
description?: string;
|
|
2497
|
+
}
|
|
2498
|
+
/** The QTI region shape vocabulary shared by hotspots and scored areas. */
|
|
2499
|
+
declare const PLAYABLE_ASSESSMENT_SHAPES: readonly ['circle', 'ellipse', 'rect', 'poly', 'default'];
|
|
2500
|
+
type PlayableAssessmentShape = (typeof PLAYABLE_ASSESSMENT_SHAPES)[number];
|
|
2501
|
+
/** A clickable or associable region positioned on a graphic. */
|
|
2502
|
+
interface PlayableAssessmentHotspot {
|
|
2503
|
+
identifier: string;
|
|
2504
|
+
shape: PlayableAssessmentShape;
|
|
2505
|
+
coords: number[];
|
|
2506
|
+
/** Maximum associations this region accepts (0 or absent means unlimited). */
|
|
2507
|
+
matchMax?: number;
|
|
2508
|
+
description?: string;
|
|
2509
|
+
}
|
|
2510
|
+
interface PlayableHotspotInteraction extends PlayableAssessmentInteractionBase {
|
|
2511
|
+
type: 'hotspot';
|
|
2512
|
+
image: PlayableAssessmentGraphic;
|
|
2513
|
+
hotspots: PlayableAssessmentHotspot[];
|
|
2514
|
+
minChoices: number;
|
|
2515
|
+
/** Always a concrete cap: an authored 0 (QTI: unlimited) resolves to the region count. */
|
|
2516
|
+
maxChoices: number;
|
|
2517
|
+
}
|
|
2518
|
+
interface PlayableGapMatchChoice {
|
|
2519
|
+
identifier: string;
|
|
2520
|
+
content: string;
|
|
2521
|
+
/** Keep this token in its authored bank slot, not a passage gap. */
|
|
2522
|
+
fixed?: boolean;
|
|
2523
|
+
/** Times this token may be placed (0 means unlimited). */
|
|
2524
|
+
matchMax: number;
|
|
2525
|
+
}
|
|
2526
|
+
interface PlayableGapMatchInteraction extends PlayableAssessmentInteractionBase {
|
|
2527
|
+
type: 'gap-match';
|
|
2528
|
+
/** Shuffle the token bank without moving passage gaps. */
|
|
2529
|
+
shuffle: boolean;
|
|
2530
|
+
/** Passage containing each gap as a `___` blank, in `gaps` order. */
|
|
2531
|
+
content: string;
|
|
2532
|
+
/** Structured passage retaining each named gap at its authored inline position. */
|
|
2533
|
+
contentNodes: PlayableContentNode[];
|
|
2534
|
+
gapTexts: PlayableGapMatchChoice[];
|
|
2535
|
+
/** Ordered gap identifiers matching the passage blanks. */
|
|
2536
|
+
gaps: string[];
|
|
2537
|
+
/** Overall pair limit (0 means unlimited). */
|
|
2538
|
+
maxAssociations: number;
|
|
2539
|
+
}
|
|
2540
|
+
interface PlayableGapImageChoice {
|
|
2541
|
+
identifier: string;
|
|
2542
|
+
image?: PlayableAssessmentGraphic;
|
|
2543
|
+
/** Times this token may be placed (0 means unlimited). */
|
|
2544
|
+
matchMax: number;
|
|
2545
|
+
description?: string;
|
|
2546
|
+
}
|
|
2547
|
+
interface PlayableGraphicGapMatchInteraction extends PlayableAssessmentInteractionBase {
|
|
2548
|
+
type: 'graphic-gap-match';
|
|
2549
|
+
image: PlayableAssessmentGraphic;
|
|
2550
|
+
gapImages: PlayableGapImageChoice[];
|
|
2551
|
+
hotspots: PlayableAssessmentHotspot[];
|
|
2552
|
+
/** Overall pair limit (0 means unlimited). */
|
|
2553
|
+
maxAssociations: number;
|
|
2554
|
+
}
|
|
2555
|
+
interface PlayableSelectPointInteraction extends PlayableAssessmentInteractionBase {
|
|
2556
|
+
type: 'select-point';
|
|
2557
|
+
image: PlayableAssessmentGraphic;
|
|
2558
|
+
minChoices: number;
|
|
2559
|
+
maxChoices: number;
|
|
2560
|
+
}
|
|
2561
|
+
interface PlayableGraphicAssociateInteraction extends PlayableAssessmentInteractionBase {
|
|
2562
|
+
type: 'graphic-associate';
|
|
2563
|
+
image: PlayableAssessmentGraphic;
|
|
2564
|
+
hotspots: PlayableAssessmentHotspot[];
|
|
2565
|
+
maxAssociations: number;
|
|
2566
|
+
}
|
|
2567
|
+
interface PlayablePositionObjectInteraction extends PlayableAssessmentInteractionBase {
|
|
2568
|
+
type: 'position-object';
|
|
2569
|
+
/** Background stage the object is placed onto. */
|
|
2570
|
+
stage: PlayableAssessmentGraphic;
|
|
2571
|
+
/** The movable object image. */
|
|
2572
|
+
object: PlayableAssessmentGraphic;
|
|
2573
|
+
/** Maximum number of placements. */
|
|
2574
|
+
maxPlacements: number;
|
|
2575
|
+
}
|
|
2576
|
+
type PlayableAssessmentInteraction = PlayableChoiceInteraction | PlayableInlineChoiceInteraction | PlayableTextEntryInteraction | PlayableOrderInteraction | PlayableMatchInteraction | PlayableHottextInteraction | PlayableHotspotInteraction | PlayableGapMatchInteraction | PlayableGraphicGapMatchInteraction | PlayableSelectPointInteraction | PlayableGraphicAssociateInteraction | PlayablePositionObjectInteraction;
|
|
2577
|
+
interface PlayableAssessmentItem {
|
|
2578
|
+
identifier: string;
|
|
2579
|
+
title: string;
|
|
2580
|
+
/**
|
|
2581
|
+
* Shared item stem, flattened to plain text. Inline interactions appear as
|
|
2582
|
+
* `___`, and block interactions belong after the prompt in `interactions`
|
|
2583
|
+
* order.
|
|
2584
|
+
*
|
|
2585
|
+
* The `___` marks that a blank is there, not which interaction fills it or
|
|
2586
|
+
* exactly where it sits: word offsets into this string are not part of the
|
|
2587
|
+
* contract. Prose is translated, re-wrapped, and tokenized differently by
|
|
2588
|
+
* language — a position counted in words does not survive any of that, and
|
|
2589
|
+
* languages that do not separate words with spaces have no such position to
|
|
2590
|
+
* count. Read `promptContent` to place blanks precisely; a client that
|
|
2591
|
+
* ignores it should render inline interactions after the prompt.
|
|
2592
|
+
*/
|
|
2593
|
+
prompt: string;
|
|
2594
|
+
/**
|
|
2595
|
+
* Structured prompt content, present whenever the stem holds an inline
|
|
2596
|
+
* interaction or anything plain text would misrepresent — images, tables,
|
|
2597
|
+
* lists, MathML. Absent means `prompt` says everything.
|
|
2598
|
+
*
|
|
2599
|
+
* Inline interactions appear as `blank` nodes carrying the response
|
|
2600
|
+
* identifier they belong to.
|
|
2601
|
+
*/
|
|
2602
|
+
promptContent?: PlayableContentNode[];
|
|
2603
|
+
/** Maximum points awarded by the item's declared outcome (1 when undeclared). */
|
|
2604
|
+
maxScore: number;
|
|
2605
|
+
/** Document-ordered interactions with item-unique response identifiers. */
|
|
2606
|
+
interactions: PlayableAssessmentInteraction[];
|
|
2607
|
+
}
|
|
2608
|
+
/** Sanitized assessment content safe to return to a sandboxed game. */
|
|
2609
|
+
interface PlayableAssessment {
|
|
2610
|
+
identifier: string;
|
|
2611
|
+
/**
|
|
2612
|
+
* Schema version of this payload's shape, never of its content.
|
|
2613
|
+
*
|
|
2614
|
+
* Present so a game can tell a field the host does not send from one this
|
|
2615
|
+
* item does not use, and so a mismatch is legible in a bug report. Branch on
|
|
2616
|
+
* whether a field is present rather than on this number — the content
|
|
2617
|
+
* vocabularies are deliberately open, so feature detection survives
|
|
2618
|
+
* versions that a comparison against a fixed number does not.
|
|
2619
|
+
*/
|
|
2620
|
+
contractVersion: typeof PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION;
|
|
2621
|
+
/**
|
|
2622
|
+
* Opaque revision of this assessment's content, used to ensure a resumed
|
|
2623
|
+
* attempt loads what it started with. Changes when an author edits the
|
|
2624
|
+
* assessment; unrelated to `contractVersion`.
|
|
2625
|
+
*/
|
|
2626
|
+
contentRevision: string;
|
|
2627
|
+
title: string;
|
|
2628
|
+
items: PlayableAssessmentItem[];
|
|
2629
|
+
}
|
|
2630
|
+
interface AssessmentScore {
|
|
2631
|
+
earned: number;
|
|
2632
|
+
possible: number;
|
|
2633
|
+
/** Aggregate score normalized to the inclusive 0..1 range. */
|
|
2634
|
+
normalized: number;
|
|
2635
|
+
}
|
|
2636
|
+
type FixedAssessmentPurpose = Exclude<AssessmentPurpose, 'review'>;
|
|
2637
|
+
/** Fixed-test purposes that do not select their catalog by curriculum standard. */
|
|
2638
|
+
type UnscopedFixedAssessmentPurpose = Exclude<FixedAssessmentPurpose, 'mastery'>;
|
|
2639
|
+
/** One requested standard slot and the playable bank item selected for it. */
|
|
2640
|
+
interface AssessmentReviewItemSelection {
|
|
2641
|
+
standard: AssessmentStandardRef;
|
|
2642
|
+
itemIdentifier: string;
|
|
2643
|
+
}
|
|
2644
|
+
/** Why one requested standard could not be fully served by the current bank. */
|
|
2645
|
+
interface AssessmentReviewShortage {
|
|
2646
|
+
standard: AssessmentStandardRef;
|
|
2647
|
+
requested: number;
|
|
2648
|
+
selected: number;
|
|
2649
|
+
eligible: number;
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* Explicit review-request fulfillment. A partial result still starts a normal
|
|
2653
|
+
* assessment attempt with every selectable item; no missing standard is
|
|
2654
|
+
* silently omitted.
|
|
2655
|
+
*/
|
|
2656
|
+
type AssessmentReviewFulfillment = {
|
|
2657
|
+
status: 'complete';
|
|
2658
|
+
requestedItemCount: number;
|
|
2659
|
+
selectedItemCount: number;
|
|
2660
|
+
shortages: [];
|
|
2661
|
+
} | {
|
|
2662
|
+
status: 'partial';
|
|
2663
|
+
requestedItemCount: number;
|
|
2664
|
+
selectedItemCount: number;
|
|
2665
|
+
shortages: AssessmentReviewShortage[];
|
|
2666
|
+
};
|
|
2667
|
+
interface FixedAssessmentSelectionContext {
|
|
2668
|
+
kind: 'fixed-test';
|
|
2669
|
+
purpose: UnscopedFixedAssessmentPurpose;
|
|
2670
|
+
}
|
|
2671
|
+
/** A complete fixed quiz selected from the catalog for one canonical standard. */
|
|
2672
|
+
interface StandardQuizSelectionContext {
|
|
2673
|
+
kind: 'standard-quiz';
|
|
2674
|
+
purpose: 'mastery';
|
|
2675
|
+
standard: AssessmentStandardRef;
|
|
2676
|
+
}
|
|
2677
|
+
/** Administration policy for one requested review skill, separate from its identity. */
|
|
2678
|
+
interface AssessmentReviewStandard extends AssessmentStandardRef {
|
|
2679
|
+
/** Require this skill's second selected question, subject to the player's cap. */
|
|
2680
|
+
requireBothQuestions?: boolean;
|
|
2681
|
+
}
|
|
2682
|
+
interface StandardsReviewSelectionContext {
|
|
2683
|
+
kind: 'standards-review';
|
|
2684
|
+
purpose: 'review';
|
|
2685
|
+
standards: AssessmentReviewStandard[];
|
|
2686
|
+
candidateItemsPerStandard: number;
|
|
2687
|
+
selections: AssessmentReviewItemSelection[];
|
|
2688
|
+
fulfillment: AssessmentReviewFulfillment;
|
|
2689
|
+
}
|
|
2690
|
+
/** Immutable identity pinned when the host starts a platform-routed diagnostic. */
|
|
2691
|
+
interface PlatformRoutedDiagnosticSelectionContext {
|
|
2692
|
+
kind: 'platform-routed-diagnostic';
|
|
2693
|
+
purpose: 'diagnostic';
|
|
2694
|
+
definitionId: string;
|
|
2695
|
+
diagnosticKey: string;
|
|
2696
|
+
routingRevision: string;
|
|
2697
|
+
}
|
|
2698
|
+
/** How the host assembled the playable assessment returned for this attempt. */
|
|
2699
|
+
type AssessmentSelectionContext = FixedAssessmentSelectionContext | StandardQuizSelectionContext | StandardsReviewSelectionContext | PlatformRoutedDiagnosticSelectionContext;
|
|
2700
|
+
/** Server-derived assessment interaction model; callers select it only through purpose. */
|
|
2701
|
+
type AssessmentFlow = 'attempt-submit' | 'item-submit' | 'platform-routed-item-submit';
|
|
2702
|
+
/** Safe feedback persisted when one item is committed in an item-submit flow. */
|
|
2703
|
+
type AssessmentItemGrading = {
|
|
2704
|
+
source: 'timeback-qti';
|
|
2705
|
+
graderVersion: string;
|
|
2706
|
+
} | {
|
|
2707
|
+
source: 'platform-qti-adapter';
|
|
2708
|
+
graderVersion: string;
|
|
2709
|
+
qtiGraderVersion: string;
|
|
2710
|
+
} | {
|
|
2711
|
+
source: 'platform-artifact';
|
|
2712
|
+
artifactVersion: string;
|
|
2713
|
+
graderVersion: string;
|
|
2714
|
+
};
|
|
2715
|
+
interface AssessmentItemSubmission {
|
|
2716
|
+
/** Stable caller-generated key used to replay a lost success response. */
|
|
2717
|
+
submissionId: string;
|
|
2718
|
+
itemIdentifier: string;
|
|
2719
|
+
/** Stable administration timestamp used for exposure ordering and resume reconciliation. */
|
|
2720
|
+
submittedAt: string;
|
|
2721
|
+
/** One-based position in the validated administration sequence. */
|
|
2722
|
+
responseVersion: number;
|
|
2723
|
+
answered: boolean;
|
|
2724
|
+
score: AssessmentScore;
|
|
2725
|
+
isCorrect: boolean | null;
|
|
2726
|
+
/**
|
|
2727
|
+
* Trusted grading provenance pinned at grading time so child-result repair
|
|
2728
|
+
* reproduces the corresponding child projection. Absent only on attempts
|
|
2729
|
+
* committed before provenance was introduced.
|
|
2730
|
+
*/
|
|
2731
|
+
grading?: AssessmentItemGrading;
|
|
2732
|
+
}
|
|
2733
|
+
/** Raw answers only. Array order is the administration sequence; no grades are trusted. */
|
|
2734
|
+
interface AssessmentTranscript {
|
|
2735
|
+
version: 1;
|
|
2736
|
+
attemptId: string;
|
|
2737
|
+
/** The selected source revision, not a review subset's presentation revision. */
|
|
2738
|
+
contentRevision: string;
|
|
2739
|
+
administrations: {
|
|
2740
|
+
submissionId: string;
|
|
2741
|
+
itemIdentifier: string;
|
|
2742
|
+
routingNodeKey?: string;
|
|
2743
|
+
/** An empty response is still an administered question. */
|
|
2744
|
+
responses: Record<string, AssessmentResponseValue>;
|
|
2745
|
+
}[];
|
|
2746
|
+
}
|
|
2747
|
+
interface ResumeAssessmentInput {
|
|
2748
|
+
/** Omit to recover the parent checkpoint, or best-effort child progress if none exists. */
|
|
2749
|
+
transcript?: AssessmentTranscript;
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Canonical activity context the SDK uses to track time for this attempt. A
|
|
2753
|
+
* narrowed `ActivityData`, so it can be handed straight to the shared clock:
|
|
2754
|
+
* the course routing fields are resolved rather than optional.
|
|
2755
|
+
*/
|
|
2756
|
+
type AssessmentActivityData = Pick<ActivityData, 'activityId' | 'courseId'> & {
|
|
2757
|
+
activityName: string;
|
|
2758
|
+
grade: TimebackGrade;
|
|
2759
|
+
subject: TimebackSubject;
|
|
2760
|
+
};
|
|
2761
|
+
interface AssessmentAttemptSnapshotBase {
|
|
2762
|
+
attemptId: string;
|
|
2763
|
+
responseVersion: number;
|
|
2764
|
+
status: 'in_progress';
|
|
2765
|
+
/** Resolved server context for the shared activity-session clock, when valid. */
|
|
2766
|
+
activityData?: AssessmentActivityData;
|
|
2767
|
+
assessment: PlayableAssessment;
|
|
2768
|
+
/** Opaque, attempt-scoped authorization. Renew through start/resume, not per answer. */
|
|
2769
|
+
assessmentToken: string;
|
|
2770
|
+
transcript: AssessmentTranscript;
|
|
2771
|
+
}
|
|
2772
|
+
/** Existing fixed, mastery, and review attempt shape. */
|
|
2773
|
+
interface ConventionalAssessmentAttemptSnapshot extends AssessmentAttemptSnapshotBase {
|
|
2774
|
+
/** Historical fixed diagnostics remain attempt-submit and are never reinterpreted. */
|
|
2775
|
+
flow: Exclude<AssessmentFlow, 'platform-routed-item-submit'>;
|
|
2776
|
+
responses: AssessmentResponses;
|
|
2777
|
+
/** Ordered committed-item ledger. Empty for attempt-submit flows. */
|
|
2778
|
+
itemSubmissions: AssessmentItemSubmission[];
|
|
2779
|
+
score: AssessmentScore | null;
|
|
2780
|
+
selection: Exclude<AssessmentSelectionContext, PlatformRoutedDiagnosticSelectionContext>;
|
|
2781
|
+
}
|
|
2782
|
+
/** Safe public track progress; authored terminal semantics stay host-only until completion. */
|
|
2783
|
+
interface DiagnosticRoutingTrackSnapshot {
|
|
2784
|
+
trackKey: string;
|
|
2785
|
+
groupKey?: string;
|
|
2786
|
+
status: DiagnosticRoutingTrackStatus;
|
|
2787
|
+
administeredCount: number;
|
|
2788
|
+
}
|
|
2789
|
+
/** Safe canonical routing projection returned while a diagnostic is in progress. */
|
|
2790
|
+
interface DiagnosticRoutingSnapshot {
|
|
2791
|
+
kind: 'platform-routed-diagnostic';
|
|
2792
|
+
revision: string;
|
|
2793
|
+
status: DiagnosticRoutingStatus;
|
|
2794
|
+
next: DiagnosticRoutingNextItem | null;
|
|
2795
|
+
tracks: DiagnosticRoutingTrackSnapshot[];
|
|
2796
|
+
}
|
|
2797
|
+
/** Small semantic diagnostic result consumed by the game after finalization. */
|
|
2798
|
+
interface DiagnosticAssessmentSubmitResult {
|
|
2799
|
+
purpose: 'diagnostic';
|
|
2800
|
+
attemptId: string;
|
|
2801
|
+
submissionId: string;
|
|
2802
|
+
diagnosticKey: string;
|
|
2803
|
+
responseVersion: number;
|
|
2804
|
+
status: 'awaiting_award';
|
|
2805
|
+
tracks: DiagnosticTrackResult[];
|
|
2806
|
+
}
|
|
2807
|
+
/** Platform-routed diagnostics never expose grading, feedback, or their committed ledger. */
|
|
2808
|
+
interface PlatformRoutedDiagnosticAttemptSnapshot extends AssessmentAttemptSnapshotBase {
|
|
2809
|
+
flow: 'platform-routed-item-submit';
|
|
2810
|
+
selection: PlatformRoutedDiagnosticSelectionContext;
|
|
2811
|
+
routing: DiagnosticRoutingSnapshot;
|
|
2812
|
+
completion: null;
|
|
2813
|
+
}
|
|
2814
|
+
interface AssessmentSaveResult {
|
|
2815
|
+
attemptId: string;
|
|
2816
|
+
responseVersion: number;
|
|
2817
|
+
status: 'in_progress';
|
|
2818
|
+
responses: AssessmentResponses;
|
|
2819
|
+
transcript: AssessmentTranscript;
|
|
2820
|
+
}
|
|
2821
|
+
interface AssessmentSubmitResultBase {
|
|
2822
|
+
attemptId: string;
|
|
2823
|
+
submissionId: string;
|
|
2824
|
+
responseVersion: number;
|
|
2825
|
+
status: 'awaiting_award';
|
|
2826
|
+
responses: AssessmentResponses;
|
|
2827
|
+
score: AssessmentScore;
|
|
2828
|
+
}
|
|
2829
|
+
interface FixedAssessmentSubmitResult extends AssessmentSubmitResultBase {
|
|
2830
|
+
purpose: UnscopedFixedAssessmentPurpose;
|
|
2831
|
+
}
|
|
2832
|
+
/** Safe final item grade, exposed only after the assessment is submitted. */
|
|
2833
|
+
interface AssessmentItemOutcome {
|
|
2834
|
+
itemIdentifier: string;
|
|
2835
|
+
standard: AssessmentStandardRef;
|
|
2836
|
+
answered: boolean;
|
|
2837
|
+
score: AssessmentScore;
|
|
2838
|
+
isCorrect: boolean | null;
|
|
2839
|
+
}
|
|
2840
|
+
interface ReviewAssessmentSubmitResult extends AssessmentSubmitResultBase {
|
|
2841
|
+
purpose: 'review';
|
|
2842
|
+
/** Final outcomes in committed transcript order, excluding unused candidates. */
|
|
2843
|
+
itemOutcomes: AssessmentItemOutcome[];
|
|
2844
|
+
}
|
|
2845
|
+
interface MasteryAssessmentSubmitResult extends AssessmentSubmitResultBase {
|
|
2846
|
+
purpose: 'mastery';
|
|
2847
|
+
/** One final outcome per question in test order, including unanswered questions. */
|
|
2848
|
+
itemOutcomes: AssessmentItemOutcome[];
|
|
2849
|
+
}
|
|
2850
|
+
type ConventionalAssessmentSubmitResult = FixedAssessmentSubmitResult | ReviewAssessmentSubmitResult | MasteryAssessmentSubmitResult;
|
|
2851
|
+
type AssessmentSubmitResult = ConventionalAssessmentSubmitResult | DiagnosticAssessmentSubmitResult;
|
|
2852
|
+
type CompletedAssessmentResult<TResult extends AssessmentSubmitResult> = Omit<TResult, 'status'> & {
|
|
2853
|
+
status: 'completed';
|
|
2854
|
+
/** XP durably recorded on the authoritative OneRoster AssessmentResult. */
|
|
2855
|
+
xpAwarded: number;
|
|
2856
|
+
/**
|
|
2857
|
+
* The mastery delta durably recorded on the authoritative result after
|
|
2858
|
+
* platform bounding. Zero when the game awarded no mastery.
|
|
2859
|
+
*/
|
|
2860
|
+
masteredUnitsApplied: number;
|
|
2861
|
+
/** Course completion percentage after this award, when mastery tracking is configured. */
|
|
2862
|
+
pctCompleteApp?: number;
|
|
2863
|
+
/** Non-fatal bounding warnings raised while applying the award. */
|
|
2864
|
+
warnings?: MasteryWriteWarning[];
|
|
2865
|
+
};
|
|
2866
|
+
/** Terminal result returned only after the game's award has been recorded. */
|
|
2867
|
+
type AssessmentFinalizeResult = CompletedAssessmentResult<FixedAssessmentSubmitResult> | CompletedAssessmentResult<ReviewAssessmentSubmitResult> | CompletedAssessmentResult<MasteryAssessmentSubmitResult> | CompletedAssessmentResult<DiagnosticAssessmentSubmitResult>;
|
|
2868
|
+
/** Minimal recovery snapshot; no QTI content load is required after answers are locked. */
|
|
2869
|
+
type SettledAssessmentAttemptSnapshot = {
|
|
2870
|
+
attemptId: string;
|
|
2871
|
+
responseVersion: number;
|
|
2872
|
+
status: 'awaiting_award';
|
|
2873
|
+
result: AssessmentSubmitResult;
|
|
2874
|
+
} | {
|
|
2875
|
+
attemptId: string;
|
|
2876
|
+
responseVersion: number;
|
|
2877
|
+
status: 'completed';
|
|
2878
|
+
result: AssessmentFinalizeResult;
|
|
2879
|
+
};
|
|
2880
|
+
/** Snapshot states that are still open for responses. */
|
|
2881
|
+
type PlayableAssessmentAttemptSnapshot = ConventionalAssessmentAttemptSnapshot | PlatformRoutedDiagnosticAttemptSnapshot;
|
|
2882
|
+
type AssessmentAttemptSnapshot = PlayableAssessmentAttemptSnapshot | SettledAssessmentAttemptSnapshot;
|
|
2883
|
+
interface StartAssessmentInputBase {
|
|
2884
|
+
activityId: string;
|
|
2885
|
+
subject?: TimebackSubject;
|
|
2886
|
+
grade?: TimebackGrade;
|
|
2887
|
+
}
|
|
2888
|
+
interface StartFixedAssessmentInput extends StartAssessmentInputBase {
|
|
2889
|
+
purpose: Exclude<UnscopedFixedAssessmentPurpose, 'diagnostic'>;
|
|
2890
|
+
}
|
|
2891
|
+
interface StartDiagnosticAssessmentInput extends StartAssessmentInputBase {
|
|
2892
|
+
purpose: 'diagnostic';
|
|
2893
|
+
/** Stable authored diagnostic identity, independent of the selected QTI test identifier. */
|
|
2894
|
+
diagnosticKey: string;
|
|
2895
|
+
}
|
|
2896
|
+
interface StartMasteryAssessmentInput extends StartAssessmentInputBase {
|
|
2897
|
+
purpose: 'mastery';
|
|
2898
|
+
standard: AssessmentStandardRef;
|
|
2899
|
+
}
|
|
2900
|
+
interface StartReviewAssessmentInput extends StartAssessmentInputBase {
|
|
2901
|
+
purpose: 'review';
|
|
2902
|
+
/** Serving order; duplicate canonical skills keep their first position. Pinned on start. */
|
|
2903
|
+
standards: AssessmentReviewStandard[];
|
|
2904
|
+
/** Candidate capacity per standard; preloaded candidates are not administered automatically. */
|
|
2905
|
+
candidateItemsPerStandard?: number;
|
|
2906
|
+
}
|
|
2907
|
+
type StartAssessmentInput = StartFixedAssessmentInput | StartDiagnosticAssessmentInput | StartMasteryAssessmentInput | StartReviewAssessmentInput;
|
|
2908
|
+
/** Transport metadata for attempting a prepared new-assessment start. */
|
|
2909
|
+
interface StartAssessmentOptions {
|
|
2910
|
+
preparationReceipt?: string;
|
|
2911
|
+
}
|
|
2912
|
+
/** Result of preparing the immediate next assessment request. */
|
|
2913
|
+
type AssessmentPreparationResult = {
|
|
2914
|
+
status: 'prepared';
|
|
2915
|
+
receipt: string;
|
|
2916
|
+
/** Receipt expiry as an ISO-8601 timestamp. */
|
|
2917
|
+
expiresAt: string;
|
|
2918
|
+
} | {
|
|
2919
|
+
status: 'unprepared';
|
|
2920
|
+
reason: 'artifact_unavailable' | 'prerequisite_unavailable' | 'local_runtime';
|
|
2921
|
+
};
|
|
2922
|
+
interface SaveAssessmentInput {
|
|
2923
|
+
expectedResponseVersion: number;
|
|
2924
|
+
/** Complete active draft; success acknowledges its durable parent checkpoint. */
|
|
2925
|
+
transcript: AssessmentTranscript;
|
|
2926
|
+
}
|
|
2927
|
+
/** Best-effort mastery progress, not an answer commitment or an explicit checkpoint. */
|
|
2928
|
+
interface RecordAssessmentProgressInput {
|
|
2929
|
+
assessmentToken: string;
|
|
2930
|
+
expectedResponseVersion: number;
|
|
2931
|
+
/** Current raw draft, including the editable administration being projected. */
|
|
2932
|
+
transcript: AssessmentTranscript;
|
|
2933
|
+
itemIdentifier: string;
|
|
2934
|
+
}
|
|
2935
|
+
interface SubmitAssessmentItemInputBase {
|
|
2936
|
+
assessmentToken: string;
|
|
2937
|
+
/** The raw prefix preceding this administration (or including it for an exact retry). */
|
|
2938
|
+
transcript: AssessmentTranscript;
|
|
2939
|
+
expectedResponseVersion: number;
|
|
2940
|
+
/** Stable across every retry of this exact item submission. */
|
|
2941
|
+
submissionId: string;
|
|
2942
|
+
itemIdentifier: string;
|
|
2943
|
+
responses: Record<string, AssessmentResponseValue | null>;
|
|
2944
|
+
}
|
|
2945
|
+
interface SubmitReviewAssessmentItemInput extends SubmitAssessmentItemInputBase {
|
|
2946
|
+
routingNodeKey?: never;
|
|
2947
|
+
}
|
|
2948
|
+
interface SubmitDiagnosticAssessmentItemInput extends SubmitAssessmentItemInputBase {
|
|
2949
|
+
/** Canonical routing-context identity returned in routing.next. */
|
|
2950
|
+
routingNodeKey: string;
|
|
2951
|
+
}
|
|
2952
|
+
type SubmitAssessmentItemInput = SubmitReviewAssessmentItemInput | SubmitDiagnosticAssessmentItemInput;
|
|
2953
|
+
interface SubmitReviewAssessmentItemResult {
|
|
2954
|
+
attemptId: string;
|
|
2955
|
+
responseVersion: number;
|
|
2956
|
+
status: 'in_progress';
|
|
2957
|
+
responses: AssessmentResponses;
|
|
2958
|
+
/** Canonical ledger after commit or replay; replace local state with it. */
|
|
2959
|
+
itemSubmissions: AssessmentItemSubmission[];
|
|
2960
|
+
submission: AssessmentItemSubmission;
|
|
2961
|
+
transcript: AssessmentTranscript;
|
|
2962
|
+
}
|
|
2963
|
+
/** Safe acknowledgement for one immutable diagnostic item commitment. */
|
|
2964
|
+
interface DiagnosticAssessmentItemReceipt {
|
|
2965
|
+
submissionId: string;
|
|
2966
|
+
routingNodeKey: string;
|
|
2967
|
+
itemIdentifier: string;
|
|
2968
|
+
submittedAt: string;
|
|
2969
|
+
responseVersion: number;
|
|
2970
|
+
answered: boolean;
|
|
2971
|
+
score: AssessmentScore;
|
|
2972
|
+
isCorrect: boolean;
|
|
2973
|
+
}
|
|
2974
|
+
/** Canonical post-commit routing state plus trusted feedback for this administration. */
|
|
2975
|
+
interface SubmitDiagnosticAssessmentItemResult {
|
|
2976
|
+
attemptId: string;
|
|
2977
|
+
responseVersion: number;
|
|
2978
|
+
status: 'in_progress';
|
|
2979
|
+
routing: DiagnosticRoutingSnapshot;
|
|
2980
|
+
submission: DiagnosticAssessmentItemReceipt;
|
|
2981
|
+
transcript: AssessmentTranscript;
|
|
2982
|
+
}
|
|
2983
|
+
type SubmitAssessmentItemResult = SubmitReviewAssessmentItemResult | SubmitDiagnosticAssessmentItemResult;
|
|
2984
|
+
interface SubmitAssessmentInput {
|
|
2985
|
+
expectedResponseVersion: number;
|
|
2986
|
+
submissionId: string;
|
|
2987
|
+
transcript: AssessmentTranscript;
|
|
2988
|
+
}
|
|
2989
|
+
/** The optional mastery portion of a finalize award; at most one field is set. */
|
|
2990
|
+
interface AssessmentMasteryAward {
|
|
2991
|
+
/**
|
|
2992
|
+
* Incremental learning units mastered by this attempt. Cannot be combined
|
|
2993
|
+
* with masteredUnitsAbsolute. The platform bounds the delta against the
|
|
2994
|
+
* course's configured masterableUnits and returns any warning.
|
|
2995
|
+
*/
|
|
2996
|
+
masteredUnits?: number;
|
|
2997
|
+
/**
|
|
2998
|
+
* Absolute mastered-unit total after this attempt; the platform computes
|
|
2999
|
+
* the delta. Cannot be combined with masteredUnits.
|
|
3000
|
+
*/
|
|
3001
|
+
masteredUnitsAbsolute?: number;
|
|
3002
|
+
}
|
|
3003
|
+
interface FinalizeAssessmentInput extends AssessmentMasteryAward {
|
|
3004
|
+
submissionId: string;
|
|
3005
|
+
/** Explicit zero is an award; omission is never interpreted as zero. */
|
|
3006
|
+
xpAwarded: number;
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* The award a game supplied at finalization, retained verbatim so a retried
|
|
3010
|
+
* finalize can be told apart from a contradictory one even after bounding
|
|
3011
|
+
* changed what was actually applied.
|
|
3012
|
+
*/
|
|
3013
|
+
interface AssessmentAwardRecord extends AssessmentMasteryAward {
|
|
3014
|
+
xpAwarded: number;
|
|
3015
|
+
/** The bounded mastery delta the platform recorded for this attempt. */
|
|
3016
|
+
masteredUnitsApplied: number;
|
|
3017
|
+
pctCompleteApp?: number;
|
|
3018
|
+
warnings?: MasteryWriteWarning[];
|
|
3019
|
+
}
|
|
3020
|
+
/** One scored region for host-only point-response fixture scoring. */
|
|
3021
|
+
interface AssessmentResponseArea {
|
|
3022
|
+
shape: PlayableAssessmentShape;
|
|
3023
|
+
coords: number[];
|
|
3024
|
+
/** The region's mapped point value (1 when omitted). */
|
|
3025
|
+
value?: number;
|
|
3026
|
+
}
|
|
3027
|
+
/** One host-only assessment entry exported for the local runtime provider. */
|
|
3028
|
+
interface AssessmentRuntimeFixtureTest {
|
|
3029
|
+
id: string;
|
|
3030
|
+
assessmentKey: string;
|
|
3031
|
+
qtiTestIdentifier: string;
|
|
3032
|
+
live: boolean;
|
|
3033
|
+
/** Canonical quiz-level alignment used by mastery selection. */
|
|
3034
|
+
standard: AssessmentStandardRef | null;
|
|
3035
|
+
assessment: PlayableAssessment;
|
|
3036
|
+
/** Private comparison rules retained for hosted-equivalent local scoring when available. */
|
|
3037
|
+
scoring?: AssessmentScoringArtifactV1;
|
|
3038
|
+
/** Correct responses are retained by the host and are never returned to the game. */
|
|
3039
|
+
correctResponses: AssessmentResponses;
|
|
3040
|
+
/**
|
|
3041
|
+
* Host-only scored regions for point responses (select-point, position-object),
|
|
3042
|
+
* keyed by item then response identifier. A point response is correct when every
|
|
3043
|
+
* point lands in a distinct scored region.
|
|
3044
|
+
*/
|
|
3045
|
+
responseAreas?: Record<string, Record<string, AssessmentResponseArea[]>>;
|
|
3046
|
+
/** Host-only platform-routing sidecar for one immutable diagnostic bank revision. */
|
|
3047
|
+
diagnostic?: {
|
|
3048
|
+
diagnosticKey: string;
|
|
3049
|
+
/** Calculated from the canonical manifest when the fixture is built. */
|
|
3050
|
+
routingRevision: string;
|
|
3051
|
+
routingManifest: DiagnosticRoutingManifestV1;
|
|
3052
|
+
};
|
|
3053
|
+
/** Host-only standard index derived from this ordinary QTI test when used for review. */
|
|
3054
|
+
reviewBank?: {
|
|
3055
|
+
sourceContentRevision: string;
|
|
3056
|
+
bankRevision: string;
|
|
3057
|
+
items: {
|
|
3058
|
+
itemIdentifier: string;
|
|
3059
|
+
standards: AssessmentStandardRef[];
|
|
3060
|
+
}[];
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
/** Ordered local catalog for one course integration and assessment purpose. */
|
|
3064
|
+
interface AssessmentRuntimeFixtureCatalog {
|
|
3065
|
+
id: string;
|
|
3066
|
+
integrationId: string;
|
|
3067
|
+
purpose: AssessmentPurpose;
|
|
3068
|
+
subject: TimebackSubject;
|
|
3069
|
+
grade: TimebackGrade;
|
|
3070
|
+
tests: AssessmentRuntimeFixtureTest[];
|
|
3071
|
+
}
|
|
3072
|
+
/**
|
|
3073
|
+
* Versioned host-only bundle consumed by the local assessment runtime.
|
|
3074
|
+
*
|
|
3075
|
+
* The version tracks this bundle's own shape, including the shape of the
|
|
3076
|
+
* assessments nested inside it — a bundle is rejected outright when either
|
|
3077
|
+
* differs, so a stale export is re-generated rather than half-read.
|
|
3078
|
+
*/
|
|
3079
|
+
interface AssessmentRuntimeFixtureBundle {
|
|
3080
|
+
version: 8;
|
|
3081
|
+
gameId: string;
|
|
3082
|
+
/**
|
|
3083
|
+
* The project-config slug the CLI resolved the game by at export time.
|
|
3084
|
+
* Local dev matches on this (the platform gameId and the locally derived
|
|
3085
|
+
* id live in different namespaces, so they can never be compared).
|
|
3086
|
+
*/
|
|
3087
|
+
gameSlug?: string;
|
|
3088
|
+
exportedAt: string;
|
|
3089
|
+
catalogs: AssessmentRuntimeFixtureCatalog[];
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
/**
|
|
3093
|
+
* Must admit exactly the raster set in `QTI_RASTER_IMAGE_MIME_TYPES`
|
|
3094
|
+
* (`@playcademy/utils/timeback`), minus the legacy `image/jpg` spelling
|
|
3095
|
+
* the server never emits.
|
|
3096
|
+
*/
|
|
3097
|
+
type AssessmentAssetMimeType = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp' | 'image/bmp';
|
|
3098
|
+
type AssessmentAssetUploadStatus = 'uploaded' | 'already_exists';
|
|
3099
|
+
/** Assessment assets are production-oriented content; local sandboxes never host them. */
|
|
3100
|
+
type AssessmentAssetEnvironment = 'production' | 'staging';
|
|
3101
|
+
interface AssessmentAssetUploadResponse {
|
|
3102
|
+
status: AssessmentAssetUploadStatus;
|
|
3103
|
+
environment: AssessmentAssetEnvironment;
|
|
3104
|
+
key: string;
|
|
3105
|
+
url: string;
|
|
3106
|
+
sha256: string;
|
|
3107
|
+
mimeType: AssessmentAssetMimeType;
|
|
3108
|
+
size: number;
|
|
3109
|
+
width: number;
|
|
3110
|
+
height: number;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
20
3113
|
/**
|
|
21
3114
|
* Base error class for Cademy SDK specific errors.
|
|
22
3115
|
*/
|
|
@@ -2162,7 +5255,7 @@ declare abstract class PlaycademyBaseClient {
|
|
|
2162
5255
|
* - `me()` - Get authenticated user profile
|
|
2163
5256
|
*/
|
|
2164
5257
|
users: {
|
|
2165
|
-
me: () => Promise<
|
|
5258
|
+
me: () => Promise<AuthenticatedUser>;
|
|
2166
5259
|
};
|
|
2167
5260
|
}
|
|
2168
5261
|
|
|
@@ -3353,6 +6446,23 @@ interface PlatformTimebackUser extends PlatformTimebackUserContext {
|
|
|
3353
6446
|
}): Promise<PlatformTimebackUserContext>;
|
|
3354
6447
|
}
|
|
3355
6448
|
|
|
6449
|
+
/**
|
|
6450
|
+
* D1 Database types
|
|
6451
|
+
*/
|
|
6452
|
+
|
|
6453
|
+
|
|
6454
|
+
|
|
6455
|
+
/**
|
|
6456
|
+
* Schema information for database migrations
|
|
6457
|
+
* Generated by comparing previous and current schema snapshots
|
|
6458
|
+
*/
|
|
6459
|
+
interface SchemaInfo {
|
|
6460
|
+
/** SQL migration statements (DDL: CREATE, ALTER, etc.) */
|
|
6461
|
+
sql: string
|
|
6462
|
+
/** Hash of the schema for change detection (stringified Drizzle schema JSON) */
|
|
6463
|
+
hash: string
|
|
6464
|
+
}
|
|
6465
|
+
|
|
3356
6466
|
/**
|
|
3357
6467
|
* @fileoverview Server SDK Type Definitions
|
|
3358
6468
|
*
|
|
@@ -3779,43 +6889,43 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3779
6889
|
dev: {
|
|
3780
6890
|
status: {
|
|
3781
6891
|
apply: () => Promise<void>;
|
|
3782
|
-
get: () => Promise<
|
|
6892
|
+
get: () => Promise<DeveloperStatusValue>;
|
|
3783
6893
|
};
|
|
3784
6894
|
games: {
|
|
3785
6895
|
list: () => Promise<Game[]>;
|
|
3786
6896
|
deploymentState: (slug: string, options?: {
|
|
3787
6897
|
include?: 'schemaSnapshot';
|
|
3788
|
-
}) => Promise<
|
|
6898
|
+
}) => Promise<DeploymentStateResponse>;
|
|
3789
6899
|
deployments: (slug: string, options?: {
|
|
3790
6900
|
limit?: number;
|
|
3791
|
-
}) => Promise<
|
|
3792
|
-
reportDeployBlocked: (slug: string, report:
|
|
6901
|
+
}) => Promise<GameDeployHistoryResponse>;
|
|
6902
|
+
reportDeployBlocked: (slug: string, report: DeployBlockedReport) => Promise<{
|
|
3793
6903
|
recorded: boolean;
|
|
3794
6904
|
}>;
|
|
3795
|
-
deploymentStateBaseline: (slug: string, body:
|
|
3796
|
-
migrationRealign: (slug: string, tag: string, checksum: string) => Promise<
|
|
3797
|
-
migrationResolve: (slug: string, tag: string, resolution:
|
|
6905
|
+
deploymentStateBaseline: (slug: string, body: DeploymentStateBaselineRequest) => Promise<DeploymentStateResponse>;
|
|
6906
|
+
migrationRealign: (slug: string, tag: string, checksum: string) => Promise<MigrationRealignResponse>;
|
|
6907
|
+
migrationResolve: (slug: string, tag: string, resolution: MigrationResolution, checksum?: string) => Promise<MigrationResolveResponse>;
|
|
3798
6908
|
deploy: (slug: string, options: {
|
|
3799
6909
|
metadata?: UpsertGameMetadataInput;
|
|
3800
6910
|
file?: File | Blob | null;
|
|
3801
6911
|
worker?: WorkerDeploymentBundle;
|
|
3802
6912
|
hooks?: DevUploadHooks;
|
|
3803
6913
|
deployId?: string;
|
|
3804
|
-
database?:
|
|
3805
|
-
baseline?:
|
|
6914
|
+
database?: DeployDatabasePayload;
|
|
6915
|
+
baseline?: DeployBaseline;
|
|
3806
6916
|
buildHash?: string;
|
|
3807
6917
|
integrationsHash?: string;
|
|
3808
6918
|
pruneSecrets?: string[];
|
|
3809
|
-
source?:
|
|
6919
|
+
source?: DeploySource;
|
|
3810
6920
|
}) => Promise<Game>;
|
|
3811
|
-
seed: (slug: string, code: string, environment?: 'local' | 'staging' | 'production', secrets?: Record<string, string>) => Promise<
|
|
6921
|
+
seed: (slug: string, code: string, environment?: 'local' | 'staging' | 'production', secrets?: Record<string, string>) => Promise<SeedResponse>;
|
|
3812
6922
|
upsert: (slug: string, metadata: UpsertGameMetadataInput) => Promise<Game>;
|
|
3813
6923
|
delete: (gameId: string) => Promise<void>;
|
|
3814
6924
|
secrets: {
|
|
3815
6925
|
set: (slug: string, secrets: Record<string, string>) => Promise<string[]>;
|
|
3816
6926
|
list: (slug: string) => Promise<string[]>;
|
|
3817
6927
|
delete: (slug: string, key: string) => Promise<void>;
|
|
3818
|
-
diff: (slug: string, secrets: Record<string, string>) => Promise<
|
|
6928
|
+
diff: (slug: string, secrets: Record<string, string>) => Promise<SecretsDiffResponse>;
|
|
3819
6929
|
};
|
|
3820
6930
|
dashboard: {
|
|
3821
6931
|
deploy: (slug: string, options: {
|
|
@@ -3838,16 +6948,16 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3838
6948
|
};
|
|
3839
6949
|
};
|
|
3840
6950
|
database: {
|
|
3841
|
-
reset: (slug: string, body?:
|
|
6951
|
+
reset: (slug: string, body?: DatabaseResetRequest) => Promise<{
|
|
3842
6952
|
success: boolean;
|
|
3843
6953
|
deploymentId: string;
|
|
3844
6954
|
resetAt: string;
|
|
3845
6955
|
schemaPushed: boolean;
|
|
3846
6956
|
}>;
|
|
3847
|
-
restorePoints: (slug: string) => Promise<
|
|
6957
|
+
restorePoints: (slug: string) => Promise<GameRestorePointsResponse>;
|
|
3848
6958
|
restore: (slug: string, body: {
|
|
3849
6959
|
restorePointId: string;
|
|
3850
|
-
}) => Promise<
|
|
6960
|
+
}) => Promise<DatabaseRestoreResponse>;
|
|
3851
6961
|
};
|
|
3852
6962
|
bucket: {
|
|
3853
6963
|
list: (slug: string, prefix?: string) => Promise<BucketFile[]>;
|
|
@@ -3913,7 +7023,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3913
7023
|
get: (gameId: string, options?: {
|
|
3914
7024
|
limit?: number;
|
|
3915
7025
|
offset?: number;
|
|
3916
|
-
}) => Promise<
|
|
7026
|
+
}) => Promise<LeaderboardEntry[]>;
|
|
3917
7027
|
};
|
|
3918
7028
|
};
|
|
3919
7029
|
/**
|
|
@@ -3922,8 +7032,8 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3922
7032
|
* - `submit(leaderboardId, score)` - Submit score to leaderboard
|
|
3923
7033
|
*/
|
|
3924
7034
|
leaderboard: {
|
|
3925
|
-
fetch: (options?:
|
|
3926
|
-
getUserRank: (gameId: string, userId: string) => Promise<
|
|
7035
|
+
fetch: (options?: LeaderboardOptions) => Promise<GameLeaderboardEntry[]>;
|
|
7036
|
+
getUserRank: (gameId: string, userId: string) => Promise<UserRank | null>;
|
|
3927
7037
|
};
|
|
3928
7038
|
/**
|
|
3929
7039
|
* Open deploy provenance: which commit is running for each game.
|
|
@@ -3931,10 +7041,10 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3931
7041
|
* - `get(slug, { history })` - One game's release and recent deploys
|
|
3932
7042
|
*/
|
|
3933
7043
|
releases: {
|
|
3934
|
-
list: () => Promise<
|
|
7044
|
+
list: () => Promise<ReleasesResponse>;
|
|
3935
7045
|
get: (slug: string, options?: {
|
|
3936
7046
|
history?: number;
|
|
3937
|
-
}) => Promise<
|
|
7047
|
+
}) => Promise<GameReleaseResponse>;
|
|
3938
7048
|
};
|
|
3939
7049
|
/**
|
|
3940
7050
|
* Score submission and user score queries.
|
|
@@ -3945,7 +7055,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3945
7055
|
submit: (gameId: string, score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
|
|
3946
7056
|
getByUser: (gameId: string, userId: string, options?: {
|
|
3947
7057
|
limit?: number;
|
|
3948
|
-
}) => Promise<
|
|
7058
|
+
}) => Promise<UserScore[]>;
|
|
3949
7059
|
};
|
|
3950
7060
|
/**
|
|
3951
7061
|
* TimeBack integration for user context and course management.
|
|
@@ -3965,101 +7075,101 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
3965
7075
|
populateStudent: (names?: {
|
|
3966
7076
|
firstName?: string;
|
|
3967
7077
|
lastName?: string;
|
|
3968
|
-
}) => Promise<
|
|
7078
|
+
}) => Promise<PopulateStudentResponse>;
|
|
3969
7079
|
readonly currentRunId: string | undefined;
|
|
3970
|
-
startActivity: (_metadata:
|
|
7080
|
+
startActivity: (_metadata: ActivityData, _options?: StartActivityOptions) => StartActivityResult;
|
|
3971
7081
|
pauseActivity: () => void;
|
|
3972
7082
|
resumeActivity: () => void;
|
|
3973
|
-
endActivity: (_data:
|
|
7083
|
+
endActivity: (_data: EndActivityScoreData) => Promise<EndActivityResponse>;
|
|
3974
7084
|
management: {
|
|
3975
|
-
setup: (request:
|
|
3976
|
-
verify: (gameId: string) => Promise<
|
|
7085
|
+
setup: (request: PlatformTimebackSetupRequest) => Promise<PlatformTimebackSetupResponse>;
|
|
7086
|
+
verify: (gameId: string) => Promise<TimebackVerifyAllResponse>;
|
|
3977
7087
|
cleanup: (gameId: string) => Promise<void>;
|
|
3978
|
-
get: (gameId: string) => Promise<
|
|
3979
|
-
getRemovedIntegrations: (gameId: string) => Promise<
|
|
3980
|
-
createIntegration: (gameId: string, request:
|
|
3981
|
-
updateIntegration: (gameId: string, courseId: string, request:
|
|
3982
|
-
deactivateCourse: (gameId: string, courseId: string) => Promise<
|
|
3983
|
-
reactivateCourse: (gameId: string, courseId: string) => Promise<
|
|
3984
|
-
getIntegrationConfig: (gameId: string, courseId: string) => Promise<
|
|
3985
|
-
getConfig: (gameId: string) => Promise<
|
|
3986
|
-
exportAssessmentFixtures: (gameId: string) => Promise<
|
|
7088
|
+
get: (gameId: string) => Promise<GameTimebackIntegration[]>;
|
|
7089
|
+
getRemovedIntegrations: (gameId: string) => Promise<RemovedGameTimebackIntegration[]>;
|
|
7090
|
+
createIntegration: (gameId: string, request: CreateGameTimebackIntegrationRequest) => Promise<GameTimebackIntegration>;
|
|
7091
|
+
updateIntegration: (gameId: string, courseId: string, request: UpdateGameTimebackIntegrationRequest) => Promise<GameTimebackIntegration>;
|
|
7092
|
+
deactivateCourse: (gameId: string, courseId: string) => Promise<GameTimebackIntegration>;
|
|
7093
|
+
reactivateCourse: (gameId: string, courseId: string) => Promise<GameTimebackIntegration>;
|
|
7094
|
+
getIntegrationConfig: (gameId: string, courseId: string) => Promise<GameTimebackIntegrationConfig>;
|
|
7095
|
+
getConfig: (gameId: string) => Promise<TimebackSetupRequest['config']>;
|
|
7096
|
+
exportAssessmentFixtures: (gameId: string) => Promise<AssessmentRuntimeFixtureBundle>;
|
|
3987
7097
|
};
|
|
3988
7098
|
students: {
|
|
3989
7099
|
get: (timebackId: string, options?: TTLCacheConfig) => Promise<PlatformTimebackUserContext>;
|
|
3990
7100
|
clearCache: (timebackId?: string) => void;
|
|
3991
7101
|
};
|
|
3992
7102
|
admin: {
|
|
3993
|
-
getRoster: (gameId: string, courseId: string) => Promise<
|
|
7103
|
+
getRoster: (gameId: string, courseId: string) => Promise<TimebackRosterResponse>;
|
|
3994
7104
|
getStudentOverview: (timebackId: string, options: {
|
|
3995
7105
|
gameId: string;
|
|
3996
7106
|
courseId?: string;
|
|
3997
|
-
}) => Promise<
|
|
7107
|
+
}) => Promise<TimebackStudentOverviewResponse>;
|
|
3998
7108
|
getGameMetrics: (timebackId: string, options: {
|
|
3999
7109
|
gameId: string;
|
|
4000
7110
|
runIds?: string[];
|
|
4001
|
-
}) => Promise<
|
|
7111
|
+
}) => Promise<GameMetricsProxyResponse>;
|
|
4002
7112
|
getStudentActivity: (timebackId: string, courseId: string, options: {
|
|
4003
7113
|
gameId: string;
|
|
4004
7114
|
limit?: number;
|
|
4005
7115
|
offset?: number;
|
|
4006
|
-
}) => Promise<
|
|
4007
|
-
getGradeLevelTestResults: (gameId: string, courseId: string, studentId: string) => Promise<
|
|
4008
|
-
getGradeLevelTestReview: (gameId: string, courseId: string, resultId: string, studentId: string) => Promise<
|
|
7116
|
+
}) => Promise<TimebackStudentActivityResponse>;
|
|
7117
|
+
getGradeLevelTestResults: (gameId: string, courseId: string, studentId: string) => Promise<TimebackGradeLevelTestResultsResponse>;
|
|
7118
|
+
getGradeLevelTestReview: (gameId: string, courseId: string, resultId: string, studentId: string) => Promise<TimebackGradeLevelTestReviewResponse>;
|
|
4009
7119
|
getActivityDetail: (timebackId: string, courseId: string, activityId: string, options: {
|
|
4010
7120
|
gameId: string;
|
|
4011
7121
|
runId?: string;
|
|
4012
|
-
}) => Promise<
|
|
7122
|
+
}) => Promise<TimebackActivityDetailResponse>;
|
|
4013
7123
|
getMetricDiscrepancies: (gameId: string, courseId: string, options?: {
|
|
4014
|
-
window?:
|
|
7124
|
+
window?: TimebackDiscrepancyQueueWindow;
|
|
4015
7125
|
startDate?: string;
|
|
4016
7126
|
endDate?: string;
|
|
4017
7127
|
studentId?: string;
|
|
4018
|
-
discrepancyMetricScopes?:
|
|
7128
|
+
discrepancyMetricScopes?: GameMetricComparisonMetric[];
|
|
4019
7129
|
includeVerified?: boolean;
|
|
4020
7130
|
eventOffset?: number;
|
|
4021
|
-
}) => Promise<
|
|
4022
|
-
verifyMetricDiscrepancy: (gameId: string, courseId: string, request:
|
|
4023
|
-
grantXp: (request:
|
|
4024
|
-
adjustTime: (request:
|
|
4025
|
-
adjustMastery: (request:
|
|
4026
|
-
reconcileMasteryForConfigChange: (request:
|
|
4027
|
-
searchStudents: (gameId: string, courseId: string, query: string) => Promise<
|
|
4028
|
-
enrollStudent: (request:
|
|
4029
|
-
unenrollStudent: (request:
|
|
4030
|
-
reactivateEnrollment: (request:
|
|
7131
|
+
}) => Promise<TimebackMetricDiscrepancyQueueResponse>;
|
|
7132
|
+
verifyMetricDiscrepancy: (gameId: string, courseId: string, request: VerifyTimebackMetricDiscrepancyRequest) => Promise<VerifyTimebackMetricDiscrepancyResponse>;
|
|
7133
|
+
grantXp: (request: GrantTimebackXpRequest) => Promise<TimebackAdminMutationResponse>;
|
|
7134
|
+
adjustTime: (request: AdjustTimebackTimeRequest) => Promise<TimebackAdminMutationResponse>;
|
|
7135
|
+
adjustMastery: (request: AdjustTimebackMasteryRequest) => Promise<TimebackAdminMutationResponse>;
|
|
7136
|
+
reconcileMasteryForConfigChange: (request: ReconcileMasteryForConfigChangeRequest) => Promise<ReconcileMasteryForConfigChangeResponse>;
|
|
7137
|
+
searchStudents: (gameId: string, courseId: string, query: string) => Promise<SearchStudentsResponse>;
|
|
7138
|
+
enrollStudent: (request: EnrollStudentRequest) => Promise<EnrollStudentResponse>;
|
|
7139
|
+
unenrollStudent: (request: UnenrollStudentRequest) => Promise<TimebackAdminMutationResponse>;
|
|
7140
|
+
reactivateEnrollment: (request: ReactivateEnrollmentRequest) => Promise<TimebackAdminMutationResponse>;
|
|
4031
7141
|
};
|
|
4032
7142
|
assessments: {
|
|
4033
|
-
list: (gameId: string, courseId: string) => Promise<
|
|
7143
|
+
list: (gameId: string, courseId: string) => Promise<AssessmentSummary[]>;
|
|
4034
7144
|
create: (gameId: string, courseId: string, data: {
|
|
4035
7145
|
assessmentKey: string;
|
|
4036
7146
|
title: string;
|
|
4037
|
-
purpose: Exclude<
|
|
4038
|
-
standard?:
|
|
4039
|
-
}) => Promise<
|
|
4040
|
-
attachExisting: (gameId: string, courseId: string, manifest:
|
|
7147
|
+
purpose: Exclude<AssessmentPurpose, 'diagnostic' | 'review'>;
|
|
7148
|
+
standard?: AssessmentStandardRef;
|
|
7149
|
+
}) => Promise<AssessmentRow>;
|
|
7150
|
+
attachExisting: (gameId: string, courseId: string, manifest: AssessmentAssociationImportManifest) => Promise<AssessmentAssociationImportResponse>;
|
|
4041
7151
|
update: (gameId: string, courseId: string, testIdentifier: string, data: {
|
|
4042
7152
|
title?: string;
|
|
4043
|
-
purpose?: Exclude<
|
|
4044
|
-
standard?:
|
|
4045
|
-
diagnostic?:
|
|
4046
|
-
status?:
|
|
4047
|
-
}) => Promise<
|
|
4048
|
-
syncReviewBank: (gameId: string, courseId: string) => Promise<
|
|
7153
|
+
purpose?: Exclude<AssessmentPurpose, 'review'>;
|
|
7154
|
+
standard?: AssessmentStandardRef;
|
|
7155
|
+
diagnostic?: DiagnosticAssessmentDefinitionInput | null;
|
|
7156
|
+
status?: AssessmentStatus;
|
|
7157
|
+
}) => Promise<AssessmentRow>;
|
|
7158
|
+
syncReviewBank: (gameId: string, courseId: string) => Promise<ReviewBankSynchronizationResult>;
|
|
4049
7159
|
remove: (gameId: string, courseId: string, testIdentifier: string) => Promise<{
|
|
4050
7160
|
action: 'discarded' | 'archived';
|
|
4051
7161
|
}>;
|
|
4052
|
-
revise: (gameId: string, courseId: string, data:
|
|
4053
|
-
listTestLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<
|
|
7162
|
+
revise: (gameId: string, courseId: string, data: AssessmentRevisionRequest) => Promise<AssessmentRevisionResult>;
|
|
7163
|
+
listTestLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<QtiAssessmentTestListResponse>;
|
|
4054
7164
|
copy: (gameId: string, courseId: string, data: {
|
|
4055
7165
|
testIdentifier: string;
|
|
4056
7166
|
assessmentKey: string;
|
|
4057
|
-
purpose: Exclude<
|
|
4058
|
-
standard?:
|
|
4059
|
-
}) => Promise<
|
|
4060
|
-
listQuestions: (gameId: string, courseId: string, testIdentifier: string) => Promise<
|
|
4061
|
-
listQuestionLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<
|
|
4062
|
-
createQuestion: (gameId: string, courseId: string, testIdentifier: string, data:
|
|
7167
|
+
purpose: Exclude<AssessmentPurpose, 'diagnostic' | 'review'>;
|
|
7168
|
+
standard?: AssessmentStandardRef;
|
|
7169
|
+
}) => Promise<AssessmentRow>;
|
|
7170
|
+
listQuestions: (gameId: string, courseId: string, testIdentifier: string) => Promise<QtiTestQuestionsResponse>;
|
|
7171
|
+
listQuestionLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<QtiAssessmentItemListResponse>;
|
|
7172
|
+
createQuestion: (gameId: string, courseId: string, testIdentifier: string, data: QtiQuestionCreateInput) => Promise<QtiTestQuestionRef>;
|
|
4063
7173
|
updateQuestion: (gameId: string, courseId: string, testIdentifier: string, itemIdentifier: string, data: Record<string, unknown>) => Promise<unknown>;
|
|
4064
7174
|
removeQuestion: (gameId: string, courseId: string, testIdentifier: string, itemIdentifier: string) => Promise<{
|
|
4065
7175
|
success: boolean;
|
|
@@ -4073,7 +7183,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
4073
7183
|
}>;
|
|
4074
7184
|
};
|
|
4075
7185
|
assessmentAssets: {
|
|
4076
|
-
upload: (body: Uint8Array, sha256: string) => Promise<
|
|
7186
|
+
upload: (body: Uint8Array, sha256: string) => Promise<AssessmentAssetUploadResponse>;
|
|
4077
7187
|
};
|
|
4078
7188
|
};
|
|
4079
7189
|
/** Auto-initializes a PlaycademyInternalClient with context from the environment */
|
|
@@ -4136,4 +7246,4 @@ interface BeginInitHandshakeOptions {
|
|
|
4136
7246
|
declare function beginInitHandshake({ iframe, origin, payload, onTimeout, onSendError }: BeginInitHandshakeOptions): () => void;
|
|
4137
7247
|
|
|
4138
7248
|
export { ApiError, HANDSHAKE_MAX_DURATION_MS, HANDSHAKE_RESEND_INTERVAL_MS, INIT_WAIT_TIMEOUT_MS, MessageEvents, PlaycademyInternalClient as PlaycademyClient, PlaycademyError, PlaycademyInternalClient, beginInitHandshake, extractApiErrorInfo, isTrustedIframeMessage, messaging };
|
|
4139
|
-
export type { ApiErrorCode, ApiErrorInfo, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, BucketFilePage, BucketListPageOptions, ChildCheckpointRelay, ClientConfig, ClientEvents, CourseMastery, CourseXp, DemoEndOptions, DemoEndPayload, DevUploadEvent, DevUploadHooks, EmbedActivity, EmbedActivityAbandoned, EmbedActivityCompleted, EmbedActivityFailed, EmbedLaunchOptions, EmbedResumeEnvelope, EmbedResumeStore, EmbedSession, EmbedSessionTiming, EmbedTimebackRecording, ErrorResponseBody, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameRow as GameRecord, GameTokenResponse, GetHighestGradeMasteredOptions, GetMasteryOptions, GetXpOptions, HighestGradeMasteredResponse, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LaunchIntent, LoginResponse, MasteryResponse, MessageEventMap, ParentGameContext, ParentGameHandle, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, ScoreSubmission, StartActivityOptions, StartActivityResult, TelemetryPayload, TimebackActivityEndRelay, TimebackActivityStartRelay, TimebackEnrollment, TimebackHeartbeatRelayRequest, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserHighestGradeMastered, TimebackUserMastery, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, XpResponse };
|
|
7249
|
+
export type { ApiErrorCode, ApiErrorInfo, AssessmentAttemptSnapshot, AssessmentAwardRecord, AssessmentFinalizeResult, AssessmentFlow, AssessmentItemGrading, AssessmentItemOutcome, AssessmentItemSubmission, AssessmentMasteryAward, AssessmentPreparationResult, AssessmentResponseUpdate, AssessmentResponseValue, AssessmentResponses, AssessmentReviewStandard, AssessmentSaveResult, AssessmentScore, AssessmentStandardRef, AssessmentSubmitResult, AssessmentTranscript, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, AuthenticatedUser, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, BucketFilePage, BucketListPageOptions, ChildCheckpointRelay, ClientConfig, ClientEvents, ConventionalAssessmentAttemptSnapshot, ConventionalAssessmentSubmitResult, CourseMastery, CourseXp, DemoEndOptions, DemoEndPayload, DeploySource, DevUploadEvent, DevUploadHooks, DeveloperStatusEnumType, DeveloperStatusResponse, DeveloperStatusValue, DiagnosticAssessmentItemReceipt, DiagnosticAssessmentSubmitResult, DiagnosticRoutingSnapshot, DiagnosticRoutingTrackSnapshot, ELevel, EmbedActivity, EmbedActivityAbandoned, EmbedActivityCompleted, EmbedActivityFailed, EmbedLaunchOptions, EmbedResumeEnvelope, EmbedResumeStore, EmbedSession, EmbedSessionTiming, EmbedTimebackRecording, ErrorResponseBody, EventListeners, ExternalGame, FetchedGame, FinalizeAssessmentInput, Game, GameContextPayload, GameCourseMetrics, GameCustomHostname, GameInitUser, GameLeaderboardEntry, GameManifest, GameMetricComparisonKind, GameMetricComparisonMetric, GameMetricComparisonRow, GameMetricComparisonRowStatus, GameMetricsProxyResponse, GameMetricsResponse, GameMetricsUnsupportedReason, GamePlatform, GameRow as GameRecord, GameReleaseResponse, GameRunMetrics, GameRunMetricsComparison, GameRunMetricsComparisonStatus, GameRunMetricsComparisonSummary, GameTimebackIntegration, GameTokenResponse, GameType, GameUser, GetHighestGradeMasteredOptions, GetMasteryOptions, GetXpOptions, HighestGradeMasteredResponse, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LaunchIntent, LeaderboardEntry, LeaderboardOptions, LeaderboardTimeframe, LocalDayContext, LocalDaySource, LoginResponse, ManifestV1, ManifestV2, ManifestVersions, MasteryAssessmentSubmitResult, MasteryResponse, MessageEventMap, ParentGameContext, ParentGameHandle, PlatformRoutedDiagnosticAttemptSnapshot, PlatformRoutedDiagnosticSelectionContext, PlatformTimebackUser, PlatformTimebackUserContext, PlayableAssessment, PlayableAssessmentAttemptSnapshot, PlayableAssessmentChoice, PlayableAssessmentGraphic, PlayableAssessmentHotspot, PlayableAssessmentInteraction, PlayableAssessmentItem, PlayableChoiceInteraction, PlayableContentNode, PlayableGapMatchChoice, PlayableGapMatchInteraction, PlayableInlineChoiceInteraction, PlayableMatchChoice, PlayableMatchInteraction, PlayableOrderInteraction, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, PopulateStudentResponse, RecordAssessmentProgressInput, Release, ReleasesResponse, ResumeAssessmentInput, SaveAssessmentInput, ScoreSubmission, SettledAssessmentAttemptSnapshot, SkillMasterySource, SkillStatus, SkillStatusLookupResult, SkillStatusRequest, SkillStatusRequestingGame, SkillStatusResponse, SkillStatusResult, SkillStatusUnsupportedReason, StandardsReviewSelectionContext, StartActivityOptions, StartActivityResult, StartAssessmentInput, StartAssessmentOptions, StartDiagnosticAssessmentInput, SubmitAssessmentInput, SubmitAssessmentItemInput, SubmitAssessmentItemResult, SubmitDiagnosticAssessmentItemInput, SubmitDiagnosticAssessmentItemResult, TelemetryPayload, TimebackActivityEndRelay, TimebackActivityStartRelay, TimebackEnrollment, TimebackHeartbeatRelayRequest, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserHighestGradeMastered, TimebackUserMastery, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, UserEnrollment, UserInfo, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData, XpResponse };
|