@playcademy/sdk 0.16.1-beta.21 → 0.16.1-beta.23

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
@@ -3150,6 +3150,12 @@ function createTimebackNamespace(client) {
3150
3150
  throw new Error("activityId is required");
3151
3151
  }
3152
3152
  validateAssessmentFilters(input);
3153
+ if (input.purpose === "diagnostic" && !input.diagnosticKey?.trim()) {
3154
+ throw new Error("diagnosticKey is required for diagnostic assessments");
3155
+ }
3156
+ if (input.purpose === "diagnostic" && input.diagnosticKey.trim().length > 200) {
3157
+ throw new Error("diagnosticKey must contain at most 200 characters");
3158
+ }
3153
3159
  if (input.purpose === "review") {
3154
3160
  if (!Array.isArray(input.standards) || input.standards.length === 0) {
3155
3161
  throw new Error("standards must contain at least one standard for review");
@@ -3232,6 +3238,9 @@ function createTimebackNamespace(client) {
3232
3238
  if (!input.itemIdentifier?.trim()) {
3233
3239
  throw new Error("itemIdentifier is required");
3234
3240
  }
3241
+ if (input.routingNodeKey !== undefined && !input.routingNodeKey.trim()) {
3242
+ throw new Error("routingNodeKey must be non-empty when provided");
3243
+ }
3235
3244
  return client["requestGameBackend"](`${ASSESSMENTS_ROUTE}/${encodeURIComponent(attemptId)}/submit-item`, "POST", input);
3236
3245
  },
3237
3246
  submit: async (attemptId, input) => {
@@ -3670,7 +3679,7 @@ async function request({
3670
3679
  return rawText && rawText.length > 0 ? rawText : undefined;
3671
3680
  }
3672
3681
  // src/version.ts
3673
- var SDK_VERSION = "0.16.1-beta.21";
3682
+ var SDK_VERSION = "0.16.1-beta.23";
3674
3683
 
3675
3684
  // src/clients/base.ts
3676
3685
  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';
@@ -4021,7 +4021,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
4021
4021
  list: (gameId: string, courseId: string) => Promise<_playcademy_types.AssessmentSummary[]>;
4022
4022
  create: (gameId: string, courseId: string, data: {
4023
4023
  title: string;
4024
- purpose: _playcademy_types.AssessmentPurpose;
4024
+ purpose: Exclude<_playcademy_types.AssessmentPurpose, 'diagnostic'>;
4025
4025
  standard?: _playcademy_types.AssessmentStandardRef;
4026
4026
  }) => Promise<_playcademy_types.AssessmentRow>;
4027
4027
  attachExisting: (gameId: string, courseId: string, manifest: _playcademy_types.AssessmentAssociationImportManifest) => Promise<_playcademy_types.AssessmentAssociationImportResponse>;
@@ -4029,6 +4029,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
4029
4029
  title?: string;
4030
4030
  purpose?: _playcademy_types.AssessmentPurpose;
4031
4031
  standard?: _playcademy_types.AssessmentStandardRef;
4032
+ diagnostic?: _playcademy_types.DiagnosticAssessmentDefinitionInput | null;
4032
4033
  status?: _playcademy_types.AssessmentStatus;
4033
4034
  }) => Promise<_playcademy_types.AssessmentRow>;
4034
4035
  reorder: (gameId: string, courseId: string, purpose: _playcademy_types.AssessmentPurpose, testIdentifiers: string[]) => Promise<{
@@ -4038,7 +4039,7 @@ declare class PlaycademyInternalClient extends PlaycademyBaseClient {
4038
4039
  action: 'discarded' | 'archived';
4039
4040
  }>;
4040
4041
  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>;
4042
+ copy: (gameId: string, courseId: string, testIdentifier: string, purpose: Exclude<_playcademy_types.AssessmentPurpose, 'diagnostic'>, standard?: _playcademy_types.AssessmentStandardRef) => Promise<_playcademy_types.AssessmentRow>;
4042
4043
  listQuestions: (gameId: string, courseId: string, testIdentifier: string) => Promise<_playcademy_types.QtiTestQuestionsResponse>;
4043
4044
  listQuestionLibrary: (gameId: string, courseId: string, options?: QtiLibraryQueryOptions) => Promise<_playcademy_types.QtiAssessmentItemListResponse>;
4044
4045
  createQuestion: (gameId: string, courseId: string, testIdentifier: string, data: _playcademy_types.QtiQuestionCreateInput) => Promise<_playcademy_types.QtiTestQuestionRef>;
package/dist/internal.js CHANGED
@@ -3150,6 +3150,12 @@ function createTimebackNamespace(client) {
3150
3150
  throw new Error("activityId is required");
3151
3151
  }
3152
3152
  validateAssessmentFilters(input);
3153
+ if (input.purpose === "diagnostic" && !input.diagnosticKey?.trim()) {
3154
+ throw new Error("diagnosticKey is required for diagnostic assessments");
3155
+ }
3156
+ if (input.purpose === "diagnostic" && input.diagnosticKey.trim().length > 200) {
3157
+ throw new Error("diagnosticKey must contain at most 200 characters");
3158
+ }
3153
3159
  if (input.purpose === "review") {
3154
3160
  if (!Array.isArray(input.standards) || input.standards.length === 0) {
3155
3161
  throw new Error("standards must contain at least one standard for review");
@@ -3232,6 +3238,9 @@ function createTimebackNamespace(client) {
3232
3238
  if (!input.itemIdentifier?.trim()) {
3233
3239
  throw new Error("itemIdentifier is required");
3234
3240
  }
3241
+ if (input.routingNodeKey !== undefined && !input.routingNodeKey.trim()) {
3242
+ throw new Error("routingNodeKey must be non-empty when provided");
3243
+ }
3235
3244
  return client["requestGameBackend"](`${ASSESSMENTS_ROUTE}/${encodeURIComponent(attemptId)}/submit-item`, "POST", input);
3236
3245
  },
3237
3246
  submit: async (attemptId, input) => {
@@ -4549,7 +4558,7 @@ async function request({
4549
4558
  return rawText && rawText.length > 0 ? rawText : undefined;
4550
4559
  }
4551
4560
  // src/version.ts
4552
- var SDK_VERSION = "0.16.1-beta.21";
4561
+ var SDK_VERSION = "0.16.1-beta.23";
4553
4562
 
4554
4563
  // src/clients/base.ts
4555
4564
  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.21";
316
+ var SDK_VERSION = "0.16.1-beta.23";
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.21";
505
+ var SDK_VERSION = "0.16.1-beta.23";
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.21",
3
+ "version": "0.16.1-beta.23",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {