@playcademy/sdk 0.16.1-beta.22 → 0.16.1-beta.24

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/README.md CHANGED
@@ -122,21 +122,25 @@ Hosted assessments use the same methods locally and when deployed:
122
122
  const attempt = await client.timeback.assessments.start({
123
123
  activityId: 'math-diagnostic',
124
124
  purpose: 'diagnostic',
125
+ diagnosticKey: 'math.grade-5-placement',
125
126
  subject: 'Math',
126
127
  grade: 5,
127
128
  })
128
129
 
129
- const saved = await client.timeback.assessments.save(attempt.attemptId, {
130
- expectedResponseVersion: attempt.responseVersion,
131
- responses: {
132
- [attempt.assessment.items[0].identifier]: { RESPONSE: 'A' },
133
- },
134
- })
130
+ if (attempt.flow !== 'platform-routed-item-submit' || !attempt.routing.next) {
131
+ throw new Error('Expected an active routed diagnostic')
132
+ }
135
133
 
136
- const completed = await client.timeback.assessments.submit(attempt.attemptId, {
137
- expectedResponseVersion: saved.responseVersion,
134
+ const current = attempt.routing.next
135
+ const committed = await client.timeback.assessments.submitItem(attempt.attemptId, {
136
+ expectedResponseVersion: attempt.responseVersion,
138
137
  submissionId: crypto.randomUUID(),
138
+ routingNodeKey: current.nodeKey,
139
+ itemIdentifier: current.itemIdentifier,
140
+ responses: { RESPONSE: 'A' },
139
141
  })
142
+
143
+ // Repeat with committed.routing.next. When routing is ready, submit() only finalizes.
140
144
  ```
141
145
 
142
146
  `start()` resumes a compatible unfinished attempt or selects content for a fixed test or standards
@@ -144,20 +148,24 @@ review. Save payloads merge at both the item and response levels; omitted values
144
148
  and `null` clears one response. Keep the returned `responseVersion` and send it with the next
145
149
  mutation.
146
150
 
147
- The returned `flow` is authoritative and is derived from `purpose`; callers do not configure
151
+ The returned `flow` is authoritative and is derived from the selected assessment; callers do not configure
148
152
  navigation, feedback, and submission independently:
149
153
 
150
- - `attempt-submit` is used for diagnostic, end-of-course, and mastery attempts. Responses remain
151
- editable through `save()` until `submit()` finalizes the whole attempt and returns feedback.
154
+ - `attempt-submit` is used for end-of-course and mastery attempts. Responses remain editable
155
+ through `save()` until `submit()` finalizes the whole attempt and returns feedback.
152
156
  - `item-submit` is used for review attempts. Submit each administered item with `submitItem()`.
153
157
  That operation atomically saves the item's responses, locks them, and returns safe immediate
154
158
  feedback. Give each item request its own stable `submissionId` and reuse that ID for an ambiguous
155
159
  retry; the returned canonical `itemSubmissions` ledger identifies committed items after retry or
156
160
  resume.
157
-
158
- The host rejects `submitItem()` for attempt-submit flows, rejects `submit()` until an item-submit
159
- attempt has administered at least one item, and enforces `expectedResponseVersion` for every
160
- mutation.
161
+ - `platform-routed-item-submit` is used for adaptive diagnostics. Render only
162
+ `routing.next.itemIdentifier`, submit it with `routing.next.nodeKey`, and replace local state with
163
+ the returned canonical routing snapshot. Diagnostic receipts never reveal correctness or score,
164
+ and `submit()` becomes available only after routing reports `ready-to-complete`.
165
+
166
+ The host rejects the wrong mutation for each flow, rejects premature finalization, and enforces
167
+ `expectedResponseVersion` for every mutation. Hosted and local providers execute the same routing
168
+ manifest contract; game code never selects the provider.
161
169
 
162
170
  For local development, import the current ordered catalog before starting the dev host:
163
171
 
package/dist/index.js CHANGED
@@ -1206,8 +1206,11 @@ class ChildSession {
1206
1206
  }
1207
1207
  this.#markOpenWindowFlushable();
1208
1208
  this.#forwardDirtyWindows();
1209
- window.removeEventListener("message", this.#onChildMessage);
1210
- window.removeEventListener("pagehide", this.#onPageHide);
1209
+ const win = globalThis.window;
1210
+ if (win) {
1211
+ win.removeEventListener("message", this.#onChildMessage);
1212
+ win.removeEventListener("pagehide", this.#onPageHide);
1213
+ }
1211
1214
  messaging.unlisten("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, this.#onTokenRefresh);
1212
1215
  messaging.unlisten("PLAYCADEMY_PAUSE" /* PAUSE */, this.#onLauncherPause);
1213
1216
  messaging.unlisten("PLAYCADEMY_RESUME" /* RESUME */, this.#onLauncherResume);
@@ -3150,6 +3153,12 @@ function createTimebackNamespace(client) {
3150
3153
  throw new Error("activityId is required");
3151
3154
  }
3152
3155
  validateAssessmentFilters(input);
3156
+ if (input.purpose === "diagnostic" && !input.diagnosticKey?.trim()) {
3157
+ throw new Error("diagnosticKey is required for diagnostic assessments");
3158
+ }
3159
+ if (input.purpose === "diagnostic" && input.diagnosticKey.trim().length > 200) {
3160
+ throw new Error("diagnosticKey must contain at most 200 characters");
3161
+ }
3153
3162
  if (input.purpose === "review") {
3154
3163
  if (!Array.isArray(input.standards) || input.standards.length === 0) {
3155
3164
  throw new Error("standards must contain at least one standard for review");
@@ -3232,6 +3241,9 @@ function createTimebackNamespace(client) {
3232
3241
  if (!input.itemIdentifier?.trim()) {
3233
3242
  throw new Error("itemIdentifier is required");
3234
3243
  }
3244
+ if (input.routingNodeKey !== undefined && !input.routingNodeKey.trim()) {
3245
+ throw new Error("routingNodeKey must be non-empty when provided");
3246
+ }
3235
3247
  return client["requestGameBackend"](`${ASSESSMENTS_ROUTE}/${encodeURIComponent(attemptId)}/submit-item`, "POST", input);
3236
3248
  },
3237
3249
  submit: async (attemptId, input) => {
@@ -3670,7 +3682,7 @@ async function request({
3670
3682
  return rawText && rawText.length > 0 ? rawText : undefined;
3671
3683
  }
3672
3684
  // src/version.ts
3673
- var SDK_VERSION = "0.16.1-beta.22";
3685
+ var SDK_VERSION = "0.16.1-beta.24";
3674
3686
 
3675
3687
  // src/clients/base.ts
3676
3688
  class PlaycademyBaseClient {
@@ -1,7 +1,7 @@
1
1
  import { SchemaInfo } from '@playcademy/cloudflare';
2
2
  import { GamePermission, AUTH_PROVIDER_IDS } from '@playcademy/constants';
3
3
  import { TimebackGrade, TimebackSubject, ELevel, HeartbeatRequest, EndActivityRequest, EndActivityScoreData, EndActivityResponse, TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/types/timeback';
4
- export { AssessmentAttemptSnapshot, AssessmentFlow, AssessmentItemSubmission, AssessmentResponseUpdate, AssessmentResponseValue, AssessmentResponses, AssessmentSaveResult, AssessmentScore, AssessmentStandardRef, AssessmentSubmitResult, ELevel, PlayableAssessment, PlayableAssessmentChoice, PlayableAssessmentGraphic, PlayableAssessmentHotspot, PlayableAssessmentInteraction, PlayableAssessmentItem, PlayableContentNode, SaveAssessmentInput, StartAssessmentInput, SubmitAssessmentInput, SubmitAssessmentItemInput, SubmitAssessmentItemResult } from '@playcademy/types/timeback';
4
+ export { AssessmentAttemptSnapshot, AssessmentFlow, AssessmentItemSubmission, AssessmentResponseUpdate, AssessmentResponseValue, AssessmentResponses, AssessmentSaveResult, AssessmentScore, AssessmentStandardRef, AssessmentSubmitResult, ConventionalAssessmentAttemptSnapshot, ConventionalAssessmentSubmitResult, DiagnosticAssessmentItemReceipt, DiagnosticAssessmentSubmitResult, DiagnosticRoutingSnapshot, DiagnosticRoutingTrackSnapshot, ELevel, PlatformRoutedDiagnosticAttemptSnapshot, PlatformRoutedDiagnosticSelectionContext, PlayableAssessment, PlayableAssessmentChoice, PlayableAssessmentGraphic, PlayableAssessmentHotspot, PlayableAssessmentInteraction, PlayableAssessmentItem, PlayableContentNode, SaveAssessmentInput, StartAssessmentInput, StartDiagnosticAssessmentInput, SubmitAssessmentInput, SubmitAssessmentItemInput, SubmitAssessmentItemResult, SubmitDiagnosticAssessmentItemInput, SubmitDiagnosticAssessmentItemResult } from '@playcademy/types/timeback';
5
5
  import * as _playcademy_types from '@playcademy/types';
6
6
  import { GameManifest, LocalDayContext } from '@playcademy/types';
7
7
  export { AuthenticatedUser, DeveloperStatusEnumType, DeveloperStatusResponse, DeveloperStatusValue, GameCourseMetrics, GameLeaderboardEntry, GameManifest, GameMetricComparisonKind, GameMetricComparisonMetric, GameMetricComparisonRow, GameMetricComparisonRowStatus, GameMetricsProxyResponse, GameMetricsResponse, GameMetricsUnsupportedReason, GamePlatform, GameRunMetrics, GameRunMetricsComparison, GameRunMetricsComparisonStatus, GameRunMetricsComparisonSummary, GameTimebackIntegration, GameType, GameUser, LeaderboardEntry, LeaderboardOptions, LeaderboardTimeframe, LocalDayContext, LocalDaySource, ManifestV1, ManifestV2, ManifestVersions, PopulateStudentResponse, UserEnrollment, UserInfo, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData } from '@playcademy/types';
@@ -3659,6 +3659,11 @@ interface QtiLibraryQueryOptions {
3659
3659
  page?: number;
3660
3660
  limit?: number;
3661
3661
  }
3662
+ interface ReviewMappingUpdateResult {
3663
+ itemCount: number;
3664
+ standardCount: number;
3665
+ sourceFingerprint: string;
3666
+ }
3662
3667
 
3663
3668
  /**
3664
3669
  * Internal Playcademy SDK client with all namespaces.
@@ -4021,7 +4026,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
4021
4026
  list: (gameId: string, courseId: string) => Promise<_playcademy_types.AssessmentSummary[]>;
4022
4027
  create: (gameId: string, courseId: string, data: {
4023
4028
  title: string;
4024
- purpose: _playcademy_types.AssessmentPurpose;
4029
+ purpose: Exclude<_playcademy_types.AssessmentPurpose, 'diagnostic'>;
4025
4030
  standard?: _playcademy_types.AssessmentStandardRef;
4026
4031
  }) => Promise<_playcademy_types.AssessmentRow>;
4027
4032
  attachExisting: (gameId: string, courseId: string, manifest: _playcademy_types.AssessmentAssociationImportManifest) => Promise<_playcademy_types.AssessmentAssociationImportResponse>;
@@ -4029,8 +4034,10 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
4029
4034
  title?: string;
4030
4035
  purpose?: _playcademy_types.AssessmentPurpose;
4031
4036
  standard?: _playcademy_types.AssessmentStandardRef;
4037
+ diagnostic?: _playcademy_types.DiagnosticAssessmentDefinitionInput | null;
4032
4038
  status?: _playcademy_types.AssessmentStatus;
4033
4039
  }) => Promise<_playcademy_types.AssessmentRow>;
4040
+ updateReviewMapping: (gameId: string, courseId: string, testIdentifier: string) => Promise<ReviewMappingUpdateResult>;
4034
4041
  reorder: (gameId: string, courseId: string, purpose: _playcademy_types.AssessmentPurpose, testIdentifiers: string[]) => Promise<{
4035
4042
  success: boolean;
4036
4043
  }>;
@@ -4038,7 +4045,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
4038
4045
  action: 'discarded' | 'archived';
4039
4046
  }>;
4040
4047
  listTestLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<_playcademy_types.QtiAssessmentTestListResponse>;
4041
- copy: (gameId: string, courseId: string, testIdentifier: string, purpose: _playcademy_types.AssessmentPurpose, standard?: _playcademy_types.AssessmentStandardRef) => Promise<_playcademy_types.AssessmentRow>;
4048
+ copy: (gameId: string, courseId: string, testIdentifier: string, purpose: Exclude<_playcademy_types.AssessmentPurpose, 'diagnostic'>, standard?: _playcademy_types.AssessmentStandardRef) => Promise<_playcademy_types.AssessmentRow>;
4042
4049
  listQuestions: (gameId: string, courseId: string, testIdentifier: string) => Promise<_playcademy_types.QtiTestQuestionsResponse>;
4043
4050
  listQuestionLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<_playcademy_types.QtiAssessmentItemListResponse>;
4044
4051
  createQuestion: (gameId: string, courseId: string, testIdentifier: string, data: _playcademy_types.QtiQuestionCreateInput) => Promise<_playcademy_types.QtiTestQuestionRef>;
package/dist/internal.js CHANGED
@@ -1206,8 +1206,11 @@ class ChildSession {
1206
1206
  }
1207
1207
  this.#markOpenWindowFlushable();
1208
1208
  this.#forwardDirtyWindows();
1209
- window.removeEventListener("message", this.#onChildMessage);
1210
- window.removeEventListener("pagehide", this.#onPageHide);
1209
+ const win = globalThis.window;
1210
+ if (win) {
1211
+ win.removeEventListener("message", this.#onChildMessage);
1212
+ win.removeEventListener("pagehide", this.#onPageHide);
1213
+ }
1211
1214
  messaging.unlisten("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, this.#onTokenRefresh);
1212
1215
  messaging.unlisten("PLAYCADEMY_PAUSE" /* PAUSE */, this.#onLauncherPause);
1213
1216
  messaging.unlisten("PLAYCADEMY_RESUME" /* RESUME */, this.#onLauncherResume);
@@ -3150,6 +3153,12 @@ function createTimebackNamespace(client) {
3150
3153
  throw new Error("activityId is required");
3151
3154
  }
3152
3155
  validateAssessmentFilters(input);
3156
+ if (input.purpose === "diagnostic" && !input.diagnosticKey?.trim()) {
3157
+ throw new Error("diagnosticKey is required for diagnostic assessments");
3158
+ }
3159
+ if (input.purpose === "diagnostic" && input.diagnosticKey.trim().length > 200) {
3160
+ throw new Error("diagnosticKey must contain at most 200 characters");
3161
+ }
3153
3162
  if (input.purpose === "review") {
3154
3163
  if (!Array.isArray(input.standards) || input.standards.length === 0) {
3155
3164
  throw new Error("standards must contain at least one standard for review");
@@ -3232,6 +3241,9 @@ function createTimebackNamespace(client) {
3232
3241
  if (!input.itemIdentifier?.trim()) {
3233
3242
  throw new Error("itemIdentifier is required");
3234
3243
  }
3244
+ if (input.routingNodeKey !== undefined && !input.routingNodeKey.trim()) {
3245
+ throw new Error("routingNodeKey must be non-empty when provided");
3246
+ }
3235
3247
  return client["requestGameBackend"](`${ASSESSMENTS_ROUTE}/${encodeURIComponent(attemptId)}/submit-item`, "POST", input);
3236
3248
  },
3237
3249
  submit: async (attemptId, input) => {
@@ -4111,6 +4123,7 @@ function createAssessmentNamespace(client) {
4111
4123
  update: (gameId, courseId, testIdentifier, data) => client["request"](assessmentPath(gameId, courseId, testIdentifier), "PATCH", {
4112
4124
  body: data
4113
4125
  }),
4126
+ updateReviewMapping: (gameId, courseId, testIdentifier) => client["request"](assessmentPath(gameId, courseId, testIdentifier, "review-mapping"), "POST"),
4114
4127
  reorder: (gameId, courseId, purpose, testIdentifiers) => client["request"](assessmentPath(gameId, courseId, "order"), "PUT", {
4115
4128
  body: { purpose, testIdentifiers }
4116
4129
  }),
@@ -4549,7 +4562,7 @@ async function request({
4549
4562
  return rawText && rawText.length > 0 ? rawText : undefined;
4550
4563
  }
4551
4564
  // src/version.ts
4552
- var SDK_VERSION = "0.16.1-beta.22";
4565
+ var SDK_VERSION = "0.16.1-beta.24";
4553
4566
 
4554
4567
  // src/clients/base.ts
4555
4568
  class PlaycademyBaseClient {
@@ -313,7 +313,7 @@ function extractApiErrorInfo(error) {
313
313
  }
314
314
 
315
315
  // src/version.ts
316
- var SDK_VERSION = "0.16.1-beta.22";
316
+ var SDK_VERSION = "0.16.1-beta.24";
317
317
 
318
318
  // src/server/request.ts
319
319
  async function makeApiRequest(opts) {
package/dist/server.js CHANGED
@@ -502,7 +502,7 @@ function extractApiErrorInfo(error) {
502
502
  }
503
503
 
504
504
  // src/version.ts
505
- var SDK_VERSION = "0.16.1-beta.22";
505
+ var SDK_VERSION = "0.16.1-beta.24";
506
506
 
507
507
  // src/server/request.ts
508
508
  async function makeApiRequest(opts) {
package/dist/types.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as _playcademy_types from '@playcademy/types';
2
2
  import { GameManifest, LocalDayContext } from '@playcademy/types';
3
3
  export { AuthenticatedUser, DeveloperStatusEnumType, DeveloperStatusResponse, DeveloperStatusValue, GameCourseMetrics, GameLeaderboardEntry, GameManifest, GameMetricComparisonKind, GameMetricComparisonMetric, GameMetricComparisonRow, GameMetricComparisonRowStatus, GameMetricsProxyResponse, GameMetricsResponse, GameMetricsUnsupportedReason, GamePlatform, GameRunMetrics, GameRunMetricsComparison, GameRunMetricsComparisonStatus, GameRunMetricsComparisonSummary, GameTimebackIntegration, GameType, GameUser, LeaderboardEntry, LeaderboardOptions, LeaderboardTimeframe, LocalDayContext, LocalDaySource, ManifestV1, ManifestV2, ManifestVersions, PopulateStudentResponse, UserEnrollment, UserInfo, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData } from '@playcademy/types';
4
4
  import { TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig, TimebackGrade, TimebackSubject, ELevel, EndActivityRequest, HeartbeatRequest, EndActivityScoreData, EndActivityResponse } from '@playcademy/types/timeback';
5
- export { AssessmentAttemptSnapshot, AssessmentFlow, AssessmentItemSubmission, AssessmentResponseUpdate, AssessmentResponseValue, AssessmentResponses, AssessmentSaveResult, AssessmentScore, AssessmentStandardRef, AssessmentSubmitResult, ELevel, PlayableAssessment, PlayableAssessmentChoice, PlayableAssessmentGraphic, PlayableAssessmentHotspot, PlayableAssessmentInteraction, PlayableAssessmentItem, PlayableContentNode, SaveAssessmentInput, StartAssessmentInput, SubmitAssessmentInput, SubmitAssessmentItemInput, SubmitAssessmentItemResult } from '@playcademy/types/timeback';
5
+ export { AssessmentAttemptSnapshot, AssessmentFlow, AssessmentItemSubmission, AssessmentResponseUpdate, AssessmentResponseValue, AssessmentResponses, AssessmentSaveResult, AssessmentScore, AssessmentStandardRef, AssessmentSubmitResult, ConventionalAssessmentAttemptSnapshot, ConventionalAssessmentSubmitResult, DiagnosticAssessmentItemReceipt, DiagnosticAssessmentSubmitResult, DiagnosticRoutingSnapshot, DiagnosticRoutingTrackSnapshot, ELevel, PlatformRoutedDiagnosticAttemptSnapshot, PlatformRoutedDiagnosticSelectionContext, PlayableAssessment, PlayableAssessmentChoice, PlayableAssessmentGraphic, PlayableAssessmentHotspot, PlayableAssessmentInteraction, PlayableAssessmentItem, PlayableContentNode, SaveAssessmentInput, StartAssessmentInput, StartDiagnosticAssessmentInput, SubmitAssessmentInput, SubmitAssessmentItemInput, SubmitAssessmentItemResult, SubmitDiagnosticAssessmentItemInput, SubmitDiagnosticAssessmentItemResult } from '@playcademy/types/timeback';
6
6
  import { TimebackUserRole, UserEnrollment, UserOrganization, UserInfo } from '@playcademy/types/user';
7
7
  import { GamePermission, AUTH_PROVIDER_IDS } from '@playcademy/constants';
8
8
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sdk",
3
- "version": "0.16.1-beta.22",
3
+ "version": "0.16.1-beta.24",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {