@playcademy/sdk 0.16.1-beta.2 → 0.16.1-beta.21
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 +59 -3
- package/dist/contracts.d.ts +47 -0
- package/dist/contracts.js +23 -0
- package/dist/index.d.ts +633 -153
- package/dist/index.js +1910 -689
- package/dist/internal.d.ts +565 -25
- package/dist/internal.js +1954 -704
- package/dist/server/edge.d.ts +25 -1
- package/dist/server/edge.js +16 -2
- package/dist/server.d.ts +25 -1
- package/dist/server.js +16 -2
- package/dist/types.d.ts +627 -152
- package/package.json +7 -1
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
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
|
-
import { TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig, TimebackGrade, TimebackSubject, HeartbeatRequest } from '@playcademy/types/timeback';
|
|
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
6
|
import { TimebackUserRole, UserEnrollment, UserOrganization, UserInfo } from '@playcademy/types/user';
|
|
6
|
-
import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
|
|
7
|
+
import { GamePermission, AUTH_PROVIDER_IDS } from '@playcademy/constants';
|
|
7
8
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
8
9
|
import { DomainValidationRecords } from '@playcademy/types/game';
|
|
9
10
|
import { z } from 'zod';
|
|
@@ -30,6 +31,13 @@ interface RetryPolicy {
|
|
|
30
31
|
retryDelaysMs?: readonly number[];
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Base error class for Cademy SDK specific errors.
|
|
36
|
+
*/
|
|
37
|
+
declare class PlaycademyError extends Error {
|
|
38
|
+
constructor(message: string);
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
/**
|
|
34
42
|
* @fileoverview Playcademy Messaging System
|
|
35
43
|
*
|
|
@@ -158,11 +166,32 @@ declare enum MessageEvents {
|
|
|
158
166
|
*/
|
|
159
167
|
DEMO_END = "PLAYCADEMY_DEMO_END",
|
|
160
168
|
/**
|
|
161
|
-
* Game shares its latest TimeBack heartbeat window with the
|
|
162
|
-
* The
|
|
163
|
-
* more reliable than
|
|
169
|
+
* Game shares its latest TimeBack heartbeat window with the embedding
|
|
170
|
+
* window. The hub relays this payload during top-level page teardown
|
|
171
|
+
* (more reliable than cross-origin iframe unload events); in child mode
|
|
172
|
+
* this stream is the heartbeat's only delivery path.
|
|
164
173
|
*/
|
|
165
174
|
TIMEBACK_HEARTBEAT_RELAY = "PLAYCADEMY_TIMEBACK_HEARTBEAT_RELAY",
|
|
175
|
+
/**
|
|
176
|
+
* Game announces its tracker opened a run (startActivity).
|
|
177
|
+
* Today only child-mode games emit this: the parent opens its own
|
|
178
|
+
* Timeback run on receipt, so tracked time starts when the lesson
|
|
179
|
+
* does, not when the iframe boots.
|
|
180
|
+
*/
|
|
181
|
+
TIMEBACK_ACTIVITY_START = "PLAYCADEMY_TIMEBACK_ACTIVITY_START",
|
|
182
|
+
/**
|
|
183
|
+
* Game relays its full end-activity report instead of POSTing it to
|
|
184
|
+
* its own backend. Today only child-mode games emit this: the parent
|
|
185
|
+
* reports the result to Timeback under its own identity.
|
|
186
|
+
*/
|
|
187
|
+
TIMEBACK_ACTIVITY_END = "PLAYCADEMY_TIMEBACK_ACTIVITY_END",
|
|
188
|
+
/**
|
|
189
|
+
* Child game → parent game. An opaque checkpoint of the child's
|
|
190
|
+
* internal state, streamed during play so the parent always holds a
|
|
191
|
+
* near-current copy (the death-time postMessage hop is unreliable;
|
|
192
|
+
* continuous checkpointing is the design). Latest wins.
|
|
193
|
+
*/
|
|
194
|
+
CHECKPOINT = "PLAYCADEMY_CHECKPOINT",
|
|
166
195
|
/**
|
|
167
196
|
* Notifies about authentication state changes.
|
|
168
197
|
* Can be sent in both directions depending on auth flow.
|
|
@@ -334,6 +363,19 @@ interface DashboardThemeConfig {
|
|
|
334
363
|
/** Secondary/accent color as a hex string, e.g. '#ffd166' */
|
|
335
364
|
secondary?: string;
|
|
336
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Child-catalog contract generation (see @playcademy/sdk/contracts).
|
|
368
|
+
* The extractor is a repo-local script (run with the CLI's runtime) that
|
|
369
|
+
* prints `{ generatedFrom, deliveries }` as JSON on stdout; the CLI
|
|
370
|
+
* validates, stamps the contract fields, and writes
|
|
371
|
+
* `.playcademy/catalog.json` deterministically. The document carries no
|
|
372
|
+
* app identity — consuming repos assign the namespace key from sync
|
|
373
|
+
* provenance.
|
|
374
|
+
*/
|
|
375
|
+
interface CatalogConfig {
|
|
376
|
+
/** Path to the extractor script, relative to the config file. */
|
|
377
|
+
extractor: string;
|
|
378
|
+
}
|
|
337
379
|
/**
|
|
338
380
|
* Unified Playcademy configuration
|
|
339
381
|
* Used for playcademy.config.{js,json}
|
|
@@ -345,6 +387,13 @@ interface PlaycademyConfig {
|
|
|
345
387
|
description?: string;
|
|
346
388
|
/** Game emoji icon */
|
|
347
389
|
emoji?: string;
|
|
390
|
+
/**
|
|
391
|
+
* Browser permissions to request for the game's iframe (e.g.
|
|
392
|
+
* `['microphone']`). Opt-in per game; only `microphone` and `camera` are
|
|
393
|
+
* delegated this way. Fullscreen, autoplay, and gamepad are granted to
|
|
394
|
+
* every game automatically.
|
|
395
|
+
*/
|
|
396
|
+
permissions?: GamePermission[];
|
|
348
397
|
/** Build command to run before deployment */
|
|
349
398
|
buildCommand?: string[];
|
|
350
399
|
/** Path to build output */
|
|
@@ -359,6 +408,8 @@ interface PlaycademyConfig {
|
|
|
359
408
|
dashboard?: DashboardConfig | boolean;
|
|
360
409
|
/** Integrations (database, custom routes, external services) */
|
|
361
410
|
integrations?: IntegrationsConfig;
|
|
411
|
+
/** Child-catalog contract generation (`playcademy catalog`) */
|
|
412
|
+
catalog?: CatalogConfig;
|
|
362
413
|
}
|
|
363
414
|
|
|
364
415
|
/**
|
|
@@ -679,6 +730,8 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
|
|
|
679
730
|
interface GameMetadata {
|
|
680
731
|
description?: string;
|
|
681
732
|
emoji?: string;
|
|
733
|
+
/** Browser permissions delegated to the game's iframe (opt-in per game). */
|
|
734
|
+
permissions?: GamePermission[];
|
|
682
735
|
[key: string]: unknown;
|
|
683
736
|
}
|
|
684
737
|
/**
|
|
@@ -1228,6 +1281,8 @@ declare abstract class PlaycademyBaseClient {
|
|
|
1228
1281
|
isInIframe: boolean;
|
|
1229
1282
|
};
|
|
1230
1283
|
protected initPayload?: InitPayload;
|
|
1284
|
+
/** Memoized `client.parent` handle; built on first access in child mode. */
|
|
1285
|
+
private parentHandle?;
|
|
1231
1286
|
protected launchId?: string;
|
|
1232
1287
|
protected gameOrigin?: string;
|
|
1233
1288
|
private browserTimeZone?;
|
|
@@ -1248,6 +1303,13 @@ declare abstract class PlaycademyBaseClient {
|
|
|
1248
1303
|
* Local day context supplied by the platform during iframe initialization.
|
|
1249
1304
|
*/
|
|
1250
1305
|
get localDay(): LocalDayContext | undefined;
|
|
1306
|
+
/**
|
|
1307
|
+
* Null outside `mode: 'child'` even if a payload smuggled in a `parent`
|
|
1308
|
+
* block, so `if (client.parent)` is a reliable child-mode test. The
|
|
1309
|
+
* returned handle is the wire block plus `checkpoint()`, memoized so
|
|
1310
|
+
* `client.parent === client.parent`.
|
|
1311
|
+
*/
|
|
1312
|
+
get parent(): ParentGameHandle | null;
|
|
1251
1313
|
/**
|
|
1252
1314
|
* Sets the authentication token for API requests.
|
|
1253
1315
|
*/
|
|
@@ -1394,153 +1456,14 @@ declare function init<T extends PlaycademyBaseClient = PlaycademyBaseClient>(thi
|
|
|
1394
1456
|
declare function login(baseUrl: string, email: string, password: string): Promise<LoginResponse>;
|
|
1395
1457
|
|
|
1396
1458
|
/**
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1459
|
+
* `endActivity()`'s resolution in child mode: the report was relayed to the
|
|
1460
|
+
* parent game rather than POSTed to the platform, so no platform award data
|
|
1461
|
+
* (courseId, xpAwarded) exists. The parent reports under its own identity.
|
|
1399
1462
|
*/
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
* - `connect(provider)` - Link Discord, Google, etc. via OAuth popup
|
|
1404
|
-
*/
|
|
1405
|
-
identity: {
|
|
1406
|
-
connect: (options: AuthOptions) => Promise<AuthResult>;
|
|
1407
|
-
_getContext: () => {
|
|
1408
|
-
isInIframe: boolean;
|
|
1409
|
-
};
|
|
1410
|
-
};
|
|
1411
|
-
/**
|
|
1412
|
-
* Game runtime lifecycle and asset loading.
|
|
1413
|
-
* - `exit()` - Return to Cademy hub
|
|
1414
|
-
* - `getGameToken()` - Get short-lived auth token
|
|
1415
|
-
* - `assets.url()`, `assets.json()`, `assets.fetch()` - Load game assets
|
|
1416
|
-
* - `on('pause')`, `on('resume')` - Handle visibility changes
|
|
1417
|
-
*/
|
|
1418
|
-
runtime: {
|
|
1419
|
-
exit: () => void;
|
|
1420
|
-
onInit: (handler: (context: GameContextPayload) => void) => void;
|
|
1421
|
-
onTokenRefresh: (handler: (data: {
|
|
1422
|
-
token: string;
|
|
1423
|
-
exp: number;
|
|
1424
|
-
}) => void) => void;
|
|
1425
|
-
onPause: (handler: () => void) => void;
|
|
1426
|
-
onResume: (handler: () => void) => void;
|
|
1427
|
-
onForceExit: (handler: () => void) => void;
|
|
1428
|
-
onOverlay: (handler: (isVisible: boolean) => void) => void;
|
|
1429
|
-
ready: () => void;
|
|
1430
|
-
sendTelemetry: (data: {
|
|
1431
|
-
fps: number;
|
|
1432
|
-
mem: number;
|
|
1433
|
-
}) => void;
|
|
1434
|
-
removeListener: (eventType: MessageEvents, handler: ((context: GameContextPayload) => void) | ((data: {
|
|
1435
|
-
token: string;
|
|
1436
|
-
exp: number;
|
|
1437
|
-
}) => void) | (() => void) | ((isVisible: boolean) => void)) => void;
|
|
1438
|
-
removeAllListeners: () => void;
|
|
1439
|
-
getListenerCounts: () => Record<string, number>;
|
|
1440
|
-
assets: {
|
|
1441
|
-
url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
|
|
1442
|
-
fetch: (path: string, options?: RequestInit) => Promise<Response>;
|
|
1443
|
-
json: <T = unknown>(path: string) => Promise<T>;
|
|
1444
|
-
blob: (path: string) => Promise<Blob>;
|
|
1445
|
-
text: (path: string) => Promise<string>;
|
|
1446
|
-
arrayBuffer: (path: string) => Promise<ArrayBuffer>;
|
|
1447
|
-
};
|
|
1448
|
-
};
|
|
1449
|
-
/**
|
|
1450
|
-
* TimeBack integration for activity tracking and user context.
|
|
1451
|
-
*
|
|
1452
|
-
* User context (cached from init, refreshable):
|
|
1453
|
-
* - `user.role` - User's role (student, parent, teacher, etc.)
|
|
1454
|
-
* - `user.enrollments` - Courses the player is enrolled in for this game
|
|
1455
|
-
* - `user.refresh({ only: ['enrollments'] })` - Refresh enrollments from server
|
|
1456
|
-
* - `user.organizations` - Schools/districts the player belongs to
|
|
1457
|
-
* - `user.fetch()` - Refresh user context from server
|
|
1458
|
-
*
|
|
1459
|
-
* Activity tracking:
|
|
1460
|
-
* - `currentRunId` - Current activity run ID, or undefined when inactive
|
|
1461
|
-
* - `startActivity(metadata)` - Begin tracking an activity, return its run
|
|
1462
|
-
* ID, and automatically handle hidden-tab and visible-tab inactivity
|
|
1463
|
-
* with configurable paused-heartbeat timeout behavior
|
|
1464
|
-
* - `pauseActivity()` / `resumeActivity()` - Pause/resume timer
|
|
1465
|
-
* - `endActivity(scoreData)` - Submit activity results to TimeBack
|
|
1466
|
-
*/
|
|
1467
|
-
timeback: {
|
|
1468
|
-
assessments: {
|
|
1469
|
-
start: (input: _playcademy_types.StartAssessmentInput) => Promise<_playcademy_types.AssessmentAttemptSnapshot>;
|
|
1470
|
-
latest: (options: _playcademy_types.GetLatestAssessmentOptions) => Promise<_playcademy_types.LatestAssessmentResult | null>;
|
|
1471
|
-
get: (attemptId: string) => Promise<_playcademy_types.AssessmentAttemptSnapshot>;
|
|
1472
|
-
save: (attemptId: string, input: _playcademy_types.SaveAssessmentInput) => Promise<_playcademy_types.AssessmentSaveResult>;
|
|
1473
|
-
submit: (attemptId: string, input: _playcademy_types.SubmitAssessmentInput) => Promise<_playcademy_types.AssessmentSubmitResult>;
|
|
1474
|
-
};
|
|
1475
|
-
readonly user: TimebackUser;
|
|
1476
|
-
readonly currentRunId: string | undefined;
|
|
1477
|
-
startActivity: (metadata: _playcademy_types.ActivityData, options?: StartActivityOptions) => StartActivityResult;
|
|
1478
|
-
pauseActivity: () => void;
|
|
1479
|
-
resumeActivity: () => void;
|
|
1480
|
-
endActivity: (data: _playcademy_types.EndActivityScoreData) => Promise<_playcademy_types.EndActivityResponse>;
|
|
1481
|
-
course: {
|
|
1482
|
-
advance: (options?: {
|
|
1483
|
-
subject?: _playcademy_types.TimebackSubject;
|
|
1484
|
-
}) => Promise<_playcademy_types.AdvanceCourseResponse>;
|
|
1485
|
-
unenroll: (options?: {
|
|
1486
|
-
subject?: _playcademy_types.TimebackSubject;
|
|
1487
|
-
force?: boolean;
|
|
1488
|
-
}) => Promise<_playcademy_types.UnenrollCourseResponse>;
|
|
1489
|
-
};
|
|
1490
|
-
};
|
|
1491
|
-
/**
|
|
1492
|
-
* Game score submission and leaderboards.
|
|
1493
|
-
* - `submit(score, metadata?)` - Record a game score
|
|
1494
|
-
*/
|
|
1495
|
-
scores: {
|
|
1496
|
-
submit: (score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
|
|
1497
|
-
};
|
|
1498
|
-
/**
|
|
1499
|
-
* Read-only leaderboard access for the current game scope.
|
|
1500
|
-
* - `fetch(options?)` - Fetch leaderboard entries
|
|
1501
|
-
*/
|
|
1502
|
-
leaderboard: {
|
|
1503
|
-
fetch: (options?: _playcademy_types.LeaderboardOptions) => Promise<_playcademy_types.GameLeaderboardEntry[]>;
|
|
1504
|
-
};
|
|
1505
|
-
/**
|
|
1506
|
-
* Demo-mode helpers. Methods throw when called outside `client.mode === 'demo'`,
|
|
1507
|
-
* so callers should gate on the mode before reaching in.
|
|
1508
|
-
* - `profile.get()` - Read the anonymous demo player's profile
|
|
1509
|
-
* - `profile.update(updates)` - Update the demo player's profile (today: the required `displayName`)
|
|
1510
|
-
* - `end(score, options?)` - Signal to the parent shell that the demo has ended
|
|
1511
|
-
*/
|
|
1512
|
-
demo: {
|
|
1513
|
-
profile: {
|
|
1514
|
-
get: () => Promise<_playcademy_types.DemoProfile>;
|
|
1515
|
-
update: (updates: _playcademy_types.DemoProfileUpdate) => Promise<_playcademy_types.DemoProfile>;
|
|
1516
|
-
};
|
|
1517
|
-
end: (score: number, options?: DemoEndOptions) => void;
|
|
1518
|
-
};
|
|
1519
|
-
/**
|
|
1520
|
-
* Make requests to your game's custom backend API routes.
|
|
1521
|
-
* - `get(path)`, `post(path, body)`, `put()`, `delete()` - HTTP methods
|
|
1522
|
-
* - Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
|
|
1523
|
-
*/
|
|
1524
|
-
backend: {
|
|
1525
|
-
get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
1526
|
-
post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
1527
|
-
put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
1528
|
-
patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
1529
|
-
delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
1530
|
-
request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
1531
|
-
download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
|
|
1532
|
-
url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
|
|
1533
|
-
};
|
|
1534
|
-
/** Auto-initializes a PlaycademyClient with context from the environment */
|
|
1535
|
-
static init: typeof init;
|
|
1536
|
-
/** Authenticates a user with email and password */
|
|
1537
|
-
static login: typeof login;
|
|
1538
|
-
/** Static identity utilities for OAuth operations */
|
|
1539
|
-
static identity: {
|
|
1540
|
-
parseOAuthState: typeof parseOAuthState;
|
|
1541
|
-
};
|
|
1463
|
+
interface RelayedEndActivityResult {
|
|
1464
|
+
status: 'relayed';
|
|
1465
|
+
runId: string;
|
|
1542
1466
|
}
|
|
1543
|
-
|
|
1544
1467
|
/**
|
|
1545
1468
|
* Options for configuring activity tracking behavior.
|
|
1546
1469
|
*/
|
|
@@ -1878,8 +1801,79 @@ type TokenType = 'session' | 'apiKey' | 'gameJwt';
|
|
|
1878
1801
|
* - `'standalone'` — game is running outside any iframe (e.g. `bun run dev`
|
|
1879
1802
|
* or direct-deploy preview) with a mock token and no real platform
|
|
1880
1803
|
* context. API calls will not succeed; use this to branch UX locally.
|
|
1804
|
+
* - `'child'` — game is embedded by another game (the parent), which ran
|
|
1805
|
+
* the INIT handshake itself and included a `parent` block in the payload.
|
|
1806
|
+
* The user and token are real (the parent's own). Direct Timeback
|
|
1807
|
+
* reporting is suppressed and relayed to the parent instead; read
|
|
1808
|
+
* `client.parent` for the parent's identity and launch instructions.
|
|
1809
|
+
*/
|
|
1810
|
+
type PlaycademyMode = 'platform' | 'demo' | 'standalone' | 'child';
|
|
1811
|
+
/**
|
|
1812
|
+
* What a parent game asks a child game to deliver.
|
|
1813
|
+
*
|
|
1814
|
+
* This contract is platform-defined so any parent can launch any child
|
|
1815
|
+
* without pair-specific vocabularies. `lessonId` addresses the child's own
|
|
1816
|
+
* catalog; the child maps it to internal content. Pair-specific extras
|
|
1817
|
+
* belong in `extensions`, which the platform never interprets (the same
|
|
1818
|
+
* split LTI makes between its resource link and custom claims).
|
|
1819
|
+
*
|
|
1820
|
+
* Parent and child ship independently, so children should still validate
|
|
1821
|
+
* the intent at runtime; SDK compatibility floors manage version skew.
|
|
1822
|
+
*/
|
|
1823
|
+
interface LaunchIntent {
|
|
1824
|
+
/** The activity to deliver, addressed in the child's own catalog. */
|
|
1825
|
+
lessonId: string;
|
|
1826
|
+
/** Pedagogy stage of the lesson (the platform's E1-E4 taxonomy). */
|
|
1827
|
+
eLevel: ELevel;
|
|
1828
|
+
/** Pair-specific extras. Never interpreted by the platform. */
|
|
1829
|
+
extensions?: Record<string, unknown>;
|
|
1830
|
+
}
|
|
1831
|
+
/**
|
|
1832
|
+
* What `client.parent` actually returns: the parent's wire context plus
|
|
1833
|
+
* the child's one capability toward it. The wire block
|
|
1834
|
+
* (`ParentGameContext`) stays a pure serializable payload; this handle
|
|
1835
|
+
* wraps it. Null outside child mode, so `if (client.parent)` remains the
|
|
1836
|
+
* child-launch test.
|
|
1837
|
+
*/
|
|
1838
|
+
type ParentGameHandle = ParentGameContext & {
|
|
1839
|
+
/**
|
|
1840
|
+
* Streams an opaque checkpoint of this game's state to the parent,
|
|
1841
|
+
* so an interrupted launch can resume later (the parent hands it
|
|
1842
|
+
* back as `client.parent.resume`). Call it whenever your state
|
|
1843
|
+
* meaningfully changes — never wait for teardown, the closing-tab
|
|
1844
|
+
* message hop is unreliable. State must be JSON-serializable and
|
|
1845
|
+
* under 64KB; anything else is dropped with a warning. This is not
|
|
1846
|
+
* your save system: the parent holds it for resume only.
|
|
1847
|
+
*/
|
|
1848
|
+
checkpoint(state: unknown): void;
|
|
1849
|
+
};
|
|
1850
|
+
/**
|
|
1851
|
+
* The parent game's block in a `mode: 'child'` INIT payload. Present only
|
|
1852
|
+
* when a parent game launched this client (see the parent-child game
|
|
1853
|
+
* embedding proposal, `docs/dev/timeback/`).
|
|
1881
1854
|
*/
|
|
1882
|
-
|
|
1855
|
+
interface ParentGameContext {
|
|
1856
|
+
/** The parent game's own platform game ID (NOT this game's ID). */
|
|
1857
|
+
gameId: string;
|
|
1858
|
+
/** What the parent wants this child to deliver. */
|
|
1859
|
+
intent: LaunchIntent;
|
|
1860
|
+
/**
|
|
1861
|
+
* An earlier launch's checkpoint state, when the parent is resuming
|
|
1862
|
+
* an interrupted lesson. Opaque: this game wrote it via
|
|
1863
|
+
* `client.parent.checkpoint()`, and only this game can interpret it.
|
|
1864
|
+
* Validate it like the intent — a blob from an older build of this
|
|
1865
|
+
* game should be ignored, not trusted.
|
|
1866
|
+
*/
|
|
1867
|
+
resume?: unknown;
|
|
1868
|
+
/**
|
|
1869
|
+
* The interrupted run id, present when the parent is resuming a
|
|
1870
|
+
* lesson. Consumed by the SDK: the launch's first `startActivity()`
|
|
1871
|
+
* adopts it automatically, which is what continues the platform run.
|
|
1872
|
+
* Games never read this; pass an explicit `runId` to
|
|
1873
|
+
* `startActivity()` to start a deliberate fresh attempt instead.
|
|
1874
|
+
*/
|
|
1875
|
+
resumeRunId?: string;
|
|
1876
|
+
}
|
|
1883
1877
|
interface ClientConfig {
|
|
1884
1878
|
baseUrl: string;
|
|
1885
1879
|
gameUrl?: string;
|
|
@@ -1908,6 +1902,8 @@ interface InitPayload {
|
|
|
1908
1902
|
launchId?: string;
|
|
1909
1903
|
/** When `true`, the parent shell provides a heartbeat relay via postMessage, so the SDK can skip its own `fetch({ keepalive })` beacon on pagehide. Defaults to `false`. */
|
|
1910
1904
|
hasHeartbeatRelay?: boolean;
|
|
1905
|
+
/** Parent game context. Present only when `mode` is `'child'`. */
|
|
1906
|
+
parent?: ParentGameContext;
|
|
1911
1907
|
}
|
|
1912
1908
|
/**
|
|
1913
1909
|
* Simplified user data passed to games via InitPayload
|
|
@@ -1942,6 +1938,167 @@ interface ClientEvents {
|
|
|
1942
1938
|
};
|
|
1943
1939
|
}
|
|
1944
1940
|
|
|
1941
|
+
/**
|
|
1942
|
+
* Playcademy SDK client for game developers.
|
|
1943
|
+
* Provides namespaced access to platform features for games running inside Cademy.
|
|
1944
|
+
*/
|
|
1945
|
+
declare class PlaycademyClient extends PlaycademyBaseClient {
|
|
1946
|
+
/**
|
|
1947
|
+
* Connect external identity providers to the user's Playcademy account.
|
|
1948
|
+
* - `connect(provider)` - Link Discord, Google, etc. via OAuth popup
|
|
1949
|
+
*/
|
|
1950
|
+
identity: {
|
|
1951
|
+
connect: (options: AuthOptions) => Promise<AuthResult>;
|
|
1952
|
+
_getContext: () => {
|
|
1953
|
+
isInIframe: boolean;
|
|
1954
|
+
};
|
|
1955
|
+
};
|
|
1956
|
+
/**
|
|
1957
|
+
* Game runtime lifecycle and asset loading.
|
|
1958
|
+
* - `exit()` - Return to Cademy hub
|
|
1959
|
+
* - `getGameToken()` - Get short-lived auth token
|
|
1960
|
+
* - `assets.url()`, `assets.json()`, `assets.fetch()` - Load game assets
|
|
1961
|
+
* - `on('pause')`, `on('resume')` - Handle visibility changes
|
|
1962
|
+
*/
|
|
1963
|
+
runtime: {
|
|
1964
|
+
exit: () => void;
|
|
1965
|
+
onInit: (handler: (context: GameContextPayload) => void) => void;
|
|
1966
|
+
onTokenRefresh: (handler: (data: {
|
|
1967
|
+
token: string;
|
|
1968
|
+
exp: number;
|
|
1969
|
+
}) => void) => void;
|
|
1970
|
+
onPause: (handler: () => void) => void;
|
|
1971
|
+
onResume: (handler: () => void) => void;
|
|
1972
|
+
onForceExit: (handler: () => void) => void;
|
|
1973
|
+
onOverlay: (handler: (isVisible: boolean) => void) => void;
|
|
1974
|
+
ready: () => void;
|
|
1975
|
+
sendTelemetry: (data: {
|
|
1976
|
+
fps: number;
|
|
1977
|
+
mem: number;
|
|
1978
|
+
}) => void;
|
|
1979
|
+
removeListener: (eventType: MessageEvents, handler: ((context: GameContextPayload) => void) | ((data: {
|
|
1980
|
+
token: string;
|
|
1981
|
+
exp: number;
|
|
1982
|
+
}) => void) | (() => void) | ((isVisible: boolean) => void)) => void;
|
|
1983
|
+
removeAllListeners: () => void;
|
|
1984
|
+
getListenerCounts: () => Record<string, number>;
|
|
1985
|
+
assets: {
|
|
1986
|
+
url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
|
|
1987
|
+
fetch: (path: string, options?: RequestInit) => Promise<Response>;
|
|
1988
|
+
json: <T = unknown>(path: string) => Promise<T>;
|
|
1989
|
+
blob: (path: string) => Promise<Blob>;
|
|
1990
|
+
text: (path: string) => Promise<string>;
|
|
1991
|
+
arrayBuffer: (path: string) => Promise<ArrayBuffer>;
|
|
1992
|
+
};
|
|
1993
|
+
};
|
|
1994
|
+
/**
|
|
1995
|
+
* TimeBack integration for activity tracking and user context.
|
|
1996
|
+
*
|
|
1997
|
+
* User context (cached from init, refreshable):
|
|
1998
|
+
* - `user.role` - User's role (student, parent, teacher, etc.)
|
|
1999
|
+
* - `user.enrollments` - Courses the player is enrolled in for this game
|
|
2000
|
+
* - `user.refresh({ only: ['enrollments'] })` - Refresh enrollments from server
|
|
2001
|
+
* - `user.organizations` - Schools/districts the player belongs to
|
|
2002
|
+
* - `user.fetch()` - Refresh user context from server
|
|
2003
|
+
*
|
|
2004
|
+
* Activity tracking:
|
|
2005
|
+
* - `currentRunId` - Current activity run ID, or undefined when inactive
|
|
2006
|
+
* - `startActivity(metadata)` - Begin tracking an activity, return its run
|
|
2007
|
+
* ID, and automatically handle hidden-tab and visible-tab inactivity
|
|
2008
|
+
* with configurable paused-heartbeat timeout behavior
|
|
2009
|
+
* - `pauseActivity()` / `resumeActivity()` - Pause/resume timer
|
|
2010
|
+
* - `endActivity(scoreData)` - Submit activity results to TimeBack
|
|
2011
|
+
*/
|
|
2012
|
+
timeback: {
|
|
2013
|
+
assessments: {
|
|
2014
|
+
start: (input: _playcademy_types.StartAssessmentInput) => Promise<_playcademy_types.AssessmentAttemptSnapshot>;
|
|
2015
|
+
latest: (options: _playcademy_types.GetLatestAssessmentOptions) => Promise<_playcademy_types.LatestAssessmentResult | null>;
|
|
2016
|
+
get: (attemptId: string) => Promise<_playcademy_types.AssessmentAttemptSnapshot>;
|
|
2017
|
+
stop: (attemptId: string) => Promise<{
|
|
2018
|
+
attemptId: string;
|
|
2019
|
+
}>;
|
|
2020
|
+
save: (attemptId: string, input: _playcademy_types.SaveAssessmentInput) => Promise<_playcademy_types.AssessmentSaveResult>;
|
|
2021
|
+
submitItem: (attemptId: string, input: _playcademy_types.SubmitAssessmentItemInput) => Promise<_playcademy_types.SubmitAssessmentItemResult>;
|
|
2022
|
+
submit: (attemptId: string, input: _playcademy_types.SubmitAssessmentInput) => Promise<_playcademy_types.AssessmentSubmitResult>;
|
|
2023
|
+
};
|
|
2024
|
+
readonly user: TimebackUser;
|
|
2025
|
+
readonly currentRunId: string | undefined;
|
|
2026
|
+
startActivity: (metadata: _playcademy_types.ActivityData, options?: StartActivityOptions) => StartActivityResult;
|
|
2027
|
+
pauseActivity: () => void;
|
|
2028
|
+
resumeActivity: () => void;
|
|
2029
|
+
endActivity: (data: _playcademy_types.EndActivityScoreData) => Promise<_playcademy_types.EndActivityResponse | RelayedEndActivityResult>;
|
|
2030
|
+
course: {
|
|
2031
|
+
advance: (options?: {
|
|
2032
|
+
subject?: _playcademy_types.TimebackSubject;
|
|
2033
|
+
}) => Promise<_playcademy_types.AdvanceCourseResponse>;
|
|
2034
|
+
unenroll: (options?: {
|
|
2035
|
+
subject?: _playcademy_types.TimebackSubject;
|
|
2036
|
+
force?: boolean;
|
|
2037
|
+
}) => Promise<_playcademy_types.UnenrollCourseResponse>;
|
|
2038
|
+
};
|
|
2039
|
+
};
|
|
2040
|
+
/**
|
|
2041
|
+
* Game score submission and leaderboards.
|
|
2042
|
+
* - `submit(score, metadata?)` - Record a game score
|
|
2043
|
+
*/
|
|
2044
|
+
scores: {
|
|
2045
|
+
submit: (score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
|
|
2046
|
+
};
|
|
2047
|
+
/**
|
|
2048
|
+
* Read-only leaderboard access for the current game scope.
|
|
2049
|
+
* - `fetch(options?)` - Fetch leaderboard entries
|
|
2050
|
+
*/
|
|
2051
|
+
leaderboard: {
|
|
2052
|
+
fetch: (options?: _playcademy_types.LeaderboardOptions) => Promise<_playcademy_types.GameLeaderboardEntry[]>;
|
|
2053
|
+
};
|
|
2054
|
+
/**
|
|
2055
|
+
* Demo-mode helpers. Methods throw when called outside `client.mode === 'demo'`,
|
|
2056
|
+
* so callers should gate on the mode before reaching in.
|
|
2057
|
+
* - `profile.get()` - Read the anonymous demo player's profile
|
|
2058
|
+
* - `profile.update(updates)` - Update the demo player's profile (today: the required `displayName`)
|
|
2059
|
+
* - `end(score, options?)` - Signal to the parent shell that the demo has ended
|
|
2060
|
+
*/
|
|
2061
|
+
demo: {
|
|
2062
|
+
profile: {
|
|
2063
|
+
get: () => Promise<_playcademy_types.DemoProfile>;
|
|
2064
|
+
update: (updates: _playcademy_types.DemoProfileUpdate) => Promise<_playcademy_types.DemoProfile>;
|
|
2065
|
+
};
|
|
2066
|
+
end: (score: number, options?: DemoEndOptions) => void;
|
|
2067
|
+
};
|
|
2068
|
+
/**
|
|
2069
|
+
* Make requests to your game's custom backend API routes.
|
|
2070
|
+
* - `get(path)`, `post(path, body)`, `put()`, `delete()` - HTTP methods
|
|
2071
|
+
* - Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
|
|
2072
|
+
*/
|
|
2073
|
+
backend: {
|
|
2074
|
+
get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
2075
|
+
post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
2076
|
+
put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
2077
|
+
patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
2078
|
+
delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
2079
|
+
request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
2080
|
+
download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
|
|
2081
|
+
url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
|
|
2082
|
+
};
|
|
2083
|
+
/**
|
|
2084
|
+
* Launch other Playcademy games as embedded children (platform mode only).
|
|
2085
|
+
* - `launch({ slug, container, intent })` - Mount a child game in a nested
|
|
2086
|
+
* iframe with `mode: 'child'`; returns a session handle with `finished`
|
|
2087
|
+
* and `closed` promises and `close()`
|
|
2088
|
+
*/
|
|
2089
|
+
embed: {
|
|
2090
|
+
launch(options: EmbedLaunchOptions): EmbedSession;
|
|
2091
|
+
};
|
|
2092
|
+
/** Auto-initializes a PlaycademyClient with context from the environment */
|
|
2093
|
+
static init: typeof init;
|
|
2094
|
+
/** Authenticates a user with email and password */
|
|
2095
|
+
static login: typeof login;
|
|
2096
|
+
/** Static identity utilities for OAuth operations */
|
|
2097
|
+
static identity: {
|
|
2098
|
+
parseOAuthState: typeof parseOAuthState;
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
|
|
1945
2102
|
/**
|
|
1946
2103
|
* Event and message payload types for SDK messaging system
|
|
1947
2104
|
*/
|
|
@@ -2050,6 +2207,42 @@ interface DemoEndPayload extends DemoEndOptions {
|
|
|
2050
2207
|
}
|
|
2051
2208
|
type TimebackHeartbeatRelayRequest = Omit<HeartbeatRequest, 'gameId' | 'studentId' | 'windowStartedAtMs' | 'windowSequence'> & {
|
|
2052
2209
|
windowStartedAtMs: number;
|
|
2210
|
+
/**
|
|
2211
|
+
* Marks a closed heartbeat window from the child's 15s accounting
|
|
2212
|
+
* cadence: the window's totals are final, its key never recurs, and
|
|
2213
|
+
* the parent forwards it exactly once (retries are safe against the
|
|
2214
|
+
* server's first-write-wins window dedupe). Absent on the 1s display
|
|
2215
|
+
* snapshots of the still-open window.
|
|
2216
|
+
*/
|
|
2217
|
+
windowClosed?: boolean;
|
|
2218
|
+
};
|
|
2219
|
+
/**
|
|
2220
|
+
* Wire payload for `PLAYCADEMY_CHECKPOINT`. An opaque snapshot of the
|
|
2221
|
+
* child game's internal state; the SDK and the parent never interpret
|
|
2222
|
+
* `state`. The parent stamps the resume envelope's `childRunId` from the
|
|
2223
|
+
* activity-start announcement, so the checkpoint itself carries no ids.
|
|
2224
|
+
*/
|
|
2225
|
+
interface ChildCheckpointRelay {
|
|
2226
|
+
state: unknown;
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Wire payload for `PLAYCADEMY_TIMEBACK_ACTIVITY_START`. A child-mode game
|
|
2230
|
+
* announces that its tracker opened a run, so the parent can open its own
|
|
2231
|
+
* Timeback run at the moment the lesson actually begins.
|
|
2232
|
+
*/
|
|
2233
|
+
type TimebackActivityStartRelay = Pick<TimebackHeartbeatRelayRequest, 'runId' | 'resumeId' | 'activityData'>;
|
|
2234
|
+
/**
|
|
2235
|
+
* Wire payload for `PLAYCADEMY_TIMEBACK_ACTIVITY_END`. The same end-activity
|
|
2236
|
+
* body a platform-mode game would POST to its backend, relayed to the parent
|
|
2237
|
+
* instead. `timingData.durationSeconds` is the full active sitting and
|
|
2238
|
+
* `sessionTimingData` carries the FULL session totals: a relayed window is
|
|
2239
|
+
* never marked persisted (a postMessage hand-off proves nothing about the
|
|
2240
|
+
* parent's POST), so the parent reconciles these totals against the windows
|
|
2241
|
+
* the server confirmed before reporting the completion's remainder.
|
|
2242
|
+
*/
|
|
2243
|
+
type TimebackActivityEndRelay = Omit<EndActivityRequest, 'gameId' | 'studentId' | 'xpEarned'> & {
|
|
2244
|
+
/** The child's XP suggestion; the parent decides the actual award. */
|
|
2245
|
+
xpEarned?: number;
|
|
2053
2246
|
};
|
|
2054
2247
|
|
|
2055
2248
|
/**
|
|
@@ -2064,6 +2257,265 @@ interface GameTokenResponse {
|
|
|
2064
2257
|
baseUrl?: string;
|
|
2065
2258
|
}
|
|
2066
2259
|
|
|
2260
|
+
/**
|
|
2261
|
+
* Public types for the launch protocol's parent side: the embedded
|
|
2262
|
+
* child-game session behind `client.embed.launch()`. Only what `launch()`
|
|
2263
|
+
* callers touch lives here; the implementation and its constructor-side
|
|
2264
|
+
* plumbing contracts live in `core/launch/session.ts`.
|
|
2265
|
+
*/
|
|
2266
|
+
|
|
2267
|
+
/**
|
|
2268
|
+
* How a child launch is recorded on this game's Timeback course.
|
|
2269
|
+
* The same metadata you would give `startActivity()` if your own document
|
|
2270
|
+
* were running the lesson: the embed session runs the whole
|
|
2271
|
+
* start-through-end lifecycle for you, stamped with this.
|
|
2272
|
+
*/
|
|
2273
|
+
interface EmbedTimebackRecording {
|
|
2274
|
+
/** The activity on this game's own course the launch is recorded as. */
|
|
2275
|
+
activityId: string;
|
|
2276
|
+
/** Display name for dashboards; prettified from `activityId` when omitted. */
|
|
2277
|
+
activityName?: string;
|
|
2278
|
+
/** With `subject`, routes the recording to one of this game's courses. */
|
|
2279
|
+
grade: TimebackGrade;
|
|
2280
|
+
/** With `grade`, routes the recording to one of this game's courses. */
|
|
2281
|
+
subject: TimebackSubject;
|
|
2282
|
+
/** Course id hint, same semantics as `startActivity()`. */
|
|
2283
|
+
courseId?: string;
|
|
2284
|
+
}
|
|
2285
|
+
/**
|
|
2286
|
+
* Everything a parent needs to resume an interrupted launch later. The
|
|
2287
|
+
* parent persists this wherever the interruption demands (memory for
|
|
2288
|
+
* exit-and-return, localStorage for tab close, its backend KV for
|
|
2289
|
+
* cross-device) and hands it back via `embed.launch({ resume })`.
|
|
2290
|
+
*/
|
|
2291
|
+
interface EmbedResumeEnvelope {
|
|
2292
|
+
/**
|
|
2293
|
+
* The child's opaque checkpoint state, exactly as it last reported
|
|
2294
|
+
* it. Never introspect it: only the child can interpret its own
|
|
2295
|
+
* state, and it validates the blob on the way back in.
|
|
2296
|
+
*/
|
|
2297
|
+
state: unknown;
|
|
2298
|
+
/**
|
|
2299
|
+
* The child's own run id from the interrupted launch, when a run was
|
|
2300
|
+
* active. On resume it crosses to the child, whose SDK re-announces
|
|
2301
|
+
* it automatically at the next launch's first start; the announced match
|
|
2302
|
+
* is what makes reusing `parentRunId` safe.
|
|
2303
|
+
*/
|
|
2304
|
+
childRunId?: string;
|
|
2305
|
+
/**
|
|
2306
|
+
* The interrupted platform run, when Timeback reporting was active.
|
|
2307
|
+
* Reused (with a fresh sitting id) only when the child accepts the
|
|
2308
|
+
* resume; otherwise a fresh run is minted.
|
|
2309
|
+
*/
|
|
2310
|
+
parentRunId?: string;
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Custom persistence for resume envelopes, passed as `launch()`'s
|
|
2314
|
+
* `resume` option. The default (when `resume` is omitted) is a built-in
|
|
2315
|
+
* localStorage store keyed by user, parent game, child game, and the
|
|
2316
|
+
* intent's lesson identity; supply your own store to keep envelopes
|
|
2317
|
+
* elsewhere (for example your backend, for cross-device resume).
|
|
2318
|
+
*/
|
|
2319
|
+
interface EmbedResumeStore {
|
|
2320
|
+
/**
|
|
2321
|
+
* Returns the stored envelope for this lesson identity, or
|
|
2322
|
+
* null/undefined when there is nothing to resume. May be async; the
|
|
2323
|
+
* boot waits for it before the child's INIT is sent.
|
|
2324
|
+
*/
|
|
2325
|
+
load(): EmbedResumeEnvelope | null | undefined | Promise<EmbedResumeEnvelope | null | undefined>;
|
|
2326
|
+
/**
|
|
2327
|
+
* Persists the latest envelope. Called on every checkpoint the child
|
|
2328
|
+
* relays (envelopes are capped at 64KB) and again when run identity
|
|
2329
|
+
* is minted. Writes should be synchronous or fire-and-forget: the
|
|
2330
|
+
* SDK never blocks on them, and a throw costs that envelope's
|
|
2331
|
+
* persistence, never the launch. Returned promises are used only for
|
|
2332
|
+
* ordering: `save` and `clear` run strictly in call order, so a slow
|
|
2333
|
+
* async save cannot land after the completion's clear.
|
|
2334
|
+
*/
|
|
2335
|
+
save(envelope: EmbedResumeEnvelope): void;
|
|
2336
|
+
/** Deletes the stored envelope. Called once when the launch completes. */
|
|
2337
|
+
clear(): void;
|
|
2338
|
+
}
|
|
2339
|
+
/**
|
|
2340
|
+
* Play-time totals for a child launch, measured by the child's own
|
|
2341
|
+
* tracker (the parent's document is idle while the student plays).
|
|
2342
|
+
*/
|
|
2343
|
+
interface EmbedSessionTiming {
|
|
2344
|
+
/** Seconds of active play. */
|
|
2345
|
+
activeSeconds: number;
|
|
2346
|
+
/** Seconds the child's tracker classified as paused or inactive, when known. */
|
|
2347
|
+
inactiveSeconds?: number;
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* The launch's activity record, resolved by `session.finished`.
|
|
2351
|
+
*
|
|
2352
|
+
* `'completed'` carries the child's report and the `end()` capability;
|
|
2353
|
+
* `'abandoned'` means the session ended first (child exit or `close()`);
|
|
2354
|
+
* `'failed'` means the launch never happened. Failure is a state to
|
|
2355
|
+
* render, not an exception to catch — `finished` never rejects.
|
|
2356
|
+
*/
|
|
2357
|
+
type EmbedActivity = EmbedActivityCompleted | EmbedActivityAbandoned | EmbedActivityFailed;
|
|
2358
|
+
/** The child called `endActivity()` and its report was relayed. */
|
|
2359
|
+
interface EmbedActivityCompleted {
|
|
2360
|
+
status: 'completed';
|
|
2361
|
+
/**
|
|
2362
|
+
* The platform run this completion records under. One run ends in at
|
|
2363
|
+
* most one completion (the server dedupes on it), and a resumed launch
|
|
2364
|
+
* keeps the interrupted run's id — so this doubles as the completion's
|
|
2365
|
+
* attempt identity: feed it to whatever consumes the result and drop
|
|
2366
|
+
* anything you have seen before. Absent only for pure UX embeds
|
|
2367
|
+
* (launched without `timeback`), which record nothing.
|
|
2368
|
+
*/
|
|
2369
|
+
runId?: string;
|
|
2370
|
+
/** Correct answers, from the child's report. */
|
|
2371
|
+
correct: number;
|
|
2372
|
+
/** Total questions, from the child's report. */
|
|
2373
|
+
total: number;
|
|
2374
|
+
timing: EmbedSessionTiming;
|
|
2375
|
+
/**
|
|
2376
|
+
* The child's full relayed end-activity body: its own activity ids,
|
|
2377
|
+
* suggested XP, and extensions. Audit data — the parent decides what
|
|
2378
|
+
* actually reaches the platform, via `end()`.
|
|
2379
|
+
*/
|
|
2380
|
+
childReport: TimebackActivityEndRelay;
|
|
2381
|
+
/**
|
|
2382
|
+
* Posts the launch's completion to the parent's own backend: the
|
|
2383
|
+
* parent-minted run id, the `timeback` recording, the caller's score
|
|
2384
|
+
* and XP decision, and the child's ids as audit extensions. Requires
|
|
2385
|
+
* the `timeback` option at launch. Calling twice returns the same
|
|
2386
|
+
* promise, so a launch can never double-report from the client.
|
|
2387
|
+
*/
|
|
2388
|
+
end(scores: EndActivityScoreData): Promise<EndActivityResponse>;
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* The session ended (child exit or `close()`) before a report arrived.
|
|
2392
|
+
* Played time has already reached the platform through forwarded
|
|
2393
|
+
* heartbeats; an abandoned launch leaves no completion, exactly like a
|
|
2394
|
+
* student wandering away from any other game.
|
|
2395
|
+
*/
|
|
2396
|
+
interface EmbedActivityAbandoned {
|
|
2397
|
+
status: 'abandoned';
|
|
2398
|
+
/**
|
|
2399
|
+
* The platform run the launch was recording under, when one had
|
|
2400
|
+
* opened. Absent when the child never started an activity or the
|
|
2401
|
+
* launch was a pure UX embed.
|
|
2402
|
+
*/
|
|
2403
|
+
runId?: string;
|
|
2404
|
+
timing: EmbedSessionTiming;
|
|
2405
|
+
/**
|
|
2406
|
+
* The final resume envelope, when the child checkpointed during the
|
|
2407
|
+
* launch. Persist it (see `EmbedResumeEnvelope`) and pass it back to
|
|
2408
|
+
* `embed.launch({ resume })` to pick the lesson up later. Absent
|
|
2409
|
+
* when the child never checkpointed.
|
|
2410
|
+
*/
|
|
2411
|
+
resume?: EmbedResumeEnvelope;
|
|
2412
|
+
}
|
|
2413
|
+
/** The launch never happened: the child could not be resolved or booted. */
|
|
2414
|
+
interface EmbedActivityFailed {
|
|
2415
|
+
status: 'failed';
|
|
2416
|
+
/** Why — unknown slug, missing deployment URL, INIT error or timeout. */
|
|
2417
|
+
error: PlaycademyError;
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Handle for one embedded child-game session.
|
|
2421
|
+
*/
|
|
2422
|
+
interface EmbedSession {
|
|
2423
|
+
/** The mounted child iframe. Useful for focus management. */
|
|
2424
|
+
readonly iframe: HTMLIFrameElement;
|
|
2425
|
+
/**
|
|
2426
|
+
* The platform run this launch records under, or null before the run
|
|
2427
|
+
* opens (the child's first activity) and for pure UX embeds. Stable
|
|
2428
|
+
* once set; also echoed on the finished record, which is where most
|
|
2429
|
+
* callers should read it.
|
|
2430
|
+
*/
|
|
2431
|
+
readonly runId: string | null;
|
|
2432
|
+
/**
|
|
2433
|
+
* The latest resume envelope, live during play; null until the child
|
|
2434
|
+
* first checkpoints. Read it on your own cadence to persist
|
|
2435
|
+
* mid-lesson (for example a debounced upload to your backend), so a
|
|
2436
|
+
* closed tab can resume on another device.
|
|
2437
|
+
*/
|
|
2438
|
+
readonly checkpoint: EmbedResumeEnvelope | null;
|
|
2439
|
+
/**
|
|
2440
|
+
* Resolves the launch's activity record when it ends — the
|
|
2441
|
+
* `animation.finished` idiom. Never rejects: operational failures
|
|
2442
|
+
* resolve as `{ status: 'failed', error }`.
|
|
2443
|
+
*/
|
|
2444
|
+
readonly finished: Promise<EmbedActivity>;
|
|
2445
|
+
/**
|
|
2446
|
+
* Resolves when the session is torn down and the iframe is unmounted:
|
|
2447
|
+
* on child exit, `close()`, boot failure, or when the SDK detects the
|
|
2448
|
+
* iframe was removed from the DOM. `finished` can resolve earlier than
|
|
2449
|
+
* this (a completed child usually shows a results screen before
|
|
2450
|
+
* exiting), so use `closed` to dismiss surrounding UI.
|
|
2451
|
+
*/
|
|
2452
|
+
readonly closed: Promise<void>;
|
|
2453
|
+
/**
|
|
2454
|
+
* Tears the session down: unmounts the iframe and stops all listeners.
|
|
2455
|
+
* Resolves a still-pending `finished` as `'abandoned'`. Safe to call
|
|
2456
|
+
* more than once. Calling it is the deterministic path; the SDK also
|
|
2457
|
+
* tears down when the child exits, and detects an iframe removed
|
|
2458
|
+
* without `close()` within ~5 seconds, so nothing leaks either way.
|
|
2459
|
+
*/
|
|
2460
|
+
close(): void;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
/**
|
|
2464
|
+
* Options for `client.embed.launch()`.
|
|
2465
|
+
*/
|
|
2466
|
+
interface EmbedLaunchOptions {
|
|
2467
|
+
/** Slug of the child game to launch. Resolved to a game id at launch time. */
|
|
2468
|
+
slug: string;
|
|
2469
|
+
/** Element the child iframe is mounted into. The iframe fills it. */
|
|
2470
|
+
container: HTMLElement;
|
|
2471
|
+
/**
|
|
2472
|
+
* What the child should deliver. This is the platform-defined
|
|
2473
|
+
* {@link LaunchIntent} contract; it crosses the iframe in the INIT
|
|
2474
|
+
* payload's `parent` block and surfaces in the child as `client.parent.intent`.
|
|
2475
|
+
*/
|
|
2476
|
+
intent: LaunchIntent;
|
|
2477
|
+
/**
|
|
2478
|
+
* Whether and how an interrupted launch can resume.
|
|
2479
|
+
*
|
|
2480
|
+
* Omitted (the default): the SDK persists the child's latest
|
|
2481
|
+
* checkpoint in localStorage, keyed by user, this game, the child,
|
|
2482
|
+
* and the intent's lesson identity. The next launch with the same
|
|
2483
|
+
* identity resumes automatically; completion clears the entry. Inert
|
|
2484
|
+
* for children that never call `client.parent.checkpoint()`.
|
|
2485
|
+
*
|
|
2486
|
+
* `false`: no persistence and no automatic resume. The manual surface
|
|
2487
|
+
* (`session.checkpoint`, the abandoned outcome's `resume`) still works.
|
|
2488
|
+
*
|
|
2489
|
+
* An {@link EmbedResumeEnvelope}: fully manual, one-shot. The launch
|
|
2490
|
+
* resumes from exactly this envelope and nothing is persisted.
|
|
2491
|
+
*
|
|
2492
|
+
* An {@link EmbedResumeStore}: delegate persistence (for example to
|
|
2493
|
+
* your backend, for cross-device resume).
|
|
2494
|
+
*
|
|
2495
|
+
* Whatever the policy, the child receives only the opaque `state`
|
|
2496
|
+
* (as `client.parent.resume`) and decides whether to use it; run ids
|
|
2497
|
+
* stay parent-side and drive run continuity when the child accepts.
|
|
2498
|
+
*/
|
|
2499
|
+
resume?: false | EmbedResumeEnvelope | EmbedResumeStore;
|
|
2500
|
+
/**
|
|
2501
|
+
* Records the launch on this game's Timeback course: the metadata
|
|
2502
|
+
* you would have given `startActivity()` if your own document were
|
|
2503
|
+
* running the lesson. Stays in the parent SDK, stamping every
|
|
2504
|
+
* forwarded heartbeat and the final completion; it never crosses the
|
|
2505
|
+
* iframe. Omit for a pure UX embed.
|
|
2506
|
+
*/
|
|
2507
|
+
timeback?: EmbedTimebackRecording;
|
|
2508
|
+
/**
|
|
2509
|
+
* Overrides the child's resolved deployment URL. Intended for local
|
|
2510
|
+
* development, where the child runs on a dev server the platform
|
|
2511
|
+
* doesn't know about. With `gameUrl` set, a slug that fails to
|
|
2512
|
+
* resolve degrades to a warning (the slug stands in as the child's
|
|
2513
|
+
* game id) instead of failing the launch, so an unregistered child
|
|
2514
|
+
* still launches locally.
|
|
2515
|
+
*/
|
|
2516
|
+
gameUrl?: string;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2067
2519
|
/**
|
|
2068
2520
|
* Scores namespace types
|
|
2069
2521
|
*/
|
|
@@ -2167,6 +2619,29 @@ interface BucketFile {
|
|
|
2167
2619
|
lastModified: string;
|
|
2168
2620
|
contentType?: string;
|
|
2169
2621
|
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Options for a single-page bucket listing
|
|
2624
|
+
*/
|
|
2625
|
+
interface BucketListPageOptions {
|
|
2626
|
+
/** Restrict results to keys starting with this prefix */
|
|
2627
|
+
prefix?: string;
|
|
2628
|
+
/** Opaque continuation cursor from the previous page's result */
|
|
2629
|
+
cursor?: string;
|
|
2630
|
+
/** Page size (1-1000); the server may return fewer */
|
|
2631
|
+
limit?: number;
|
|
2632
|
+
/** Roll deeper keys into `prefixes` entries, S3 delimiter style */
|
|
2633
|
+
delimiter?: string;
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* One page of a bucket listing
|
|
2637
|
+
*/
|
|
2638
|
+
interface BucketFilePage {
|
|
2639
|
+
files: BucketFile[];
|
|
2640
|
+
/** Rolled-up common prefixes; present for delimiter listings that found any */
|
|
2641
|
+
prefixes?: string[];
|
|
2642
|
+
/** Present only when more pages remain */
|
|
2643
|
+
cursor?: string;
|
|
2644
|
+
}
|
|
2170
2645
|
/**
|
|
2171
2646
|
* KV key entry
|
|
2172
2647
|
*/
|
|
@@ -2260,4 +2735,4 @@ interface PlatformTimebackUser extends PlatformTimebackUserContext {
|
|
|
2260
2735
|
}
|
|
2261
2736
|
|
|
2262
2737
|
export { PlaycademyClient };
|
|
2263
|
-
export type { AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, ClientConfig, ClientEvents, CourseMastery, CourseXp, DemoEndOptions, DemoEndPayload, DevUploadEvent, DevUploadHooks, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameRow as GameRecord, GameTokenResponse, GetHighestGradeMasteredOptions, GetMasteryOptions, GetXpOptions, HighestGradeMasteredResponse, HostedGame, InitErrorPayload, InitPayload, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LoginResponse, MasteryResponse, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyMode, PlaycademyServerClientConfig, PlaycademyServerClientState, ScoreSubmission, StartActivityOptions, StartActivityResult, TelemetryPayload, TimebackEnrollment, TimebackHeartbeatRelayRequest, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserHighestGradeMastered, TimebackUserMastery, TimebackUserRefreshField, TimebackUserRefreshOptions, TimebackUserXp, TokenRefreshPayload, TokenType, UpsertGameMetadataInput, UserRow as User, XpResponse };
|
|
2738
|
+
export type { 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, 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, 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 };
|