@playcademy/sdk 0.1.1 → 0.1.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/README.md +40 -0
- package/dist/index.d.ts +6 -68
- package/dist/index.js +87 -17
- package/dist/server.d.ts +11 -20
- package/dist/server.js +16 -48
- package/dist/types.d.ts +6 -68
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -152,6 +152,29 @@ console.log(`Level ${userLevel.currentLevel}, ${progress.xpToNextLevel} XP to ne
|
|
|
152
152
|
// XP updates come from TimeBack webhooks only.
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
+
### TimeBack Integration
|
|
156
|
+
|
|
157
|
+
Track learning activities and automatically calculate XP based on time spent and accuracy:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Start tracking an activity (only activityId required!)
|
|
161
|
+
client.timeback.startActivity({
|
|
162
|
+
activityId: 'math-quiz-level-1',
|
|
163
|
+
})
|
|
164
|
+
// Auto-derived: activityName "Math Quiz Level 1"
|
|
165
|
+
// Auto-filled by backend: appName, subject, sensorUrl
|
|
166
|
+
|
|
167
|
+
// ... player completes activity ...
|
|
168
|
+
|
|
169
|
+
// End activity and submit results (XP calculated automatically)
|
|
170
|
+
await client.timeback.endActivity({
|
|
171
|
+
correctQuestions: 8,
|
|
172
|
+
totalQuestions: 10,
|
|
173
|
+
})
|
|
174
|
+
// XP calculation: base (1 min = 1 XP) × accuracy multiplier
|
|
175
|
+
// 100%: 1.25x | 80-99%: 1.0x | 65-79%: 0.5x | <65%: 0x
|
|
176
|
+
```
|
|
177
|
+
|
|
155
178
|
### Real-time Communication
|
|
156
179
|
|
|
157
180
|
Open a direct, low-latency communication channel for your game, perfect for multiplayer interactions.
|
|
@@ -228,6 +251,23 @@ channel.onMessage(data => {
|
|
|
228
251
|
- `open(channelName?)`: Opens a game-scoped WebSocket channel.
|
|
229
252
|
- `token.get()`: Retrieves a JWT for the real-time service.
|
|
230
253
|
|
|
254
|
+
#### **TimeBack** (`client.timeback`)
|
|
255
|
+
|
|
256
|
+
- `startActivity(metadata)`: Start tracking an activity (stores start time and metadata)
|
|
257
|
+
- `metadata.activityId`: Unique activity identifier (required)
|
|
258
|
+
- `metadata.activityName`: Human-readable activity name
|
|
259
|
+
- `metadata.subject`: Subject area (Math, Reading, Science, etc.)
|
|
260
|
+
- `metadata.appName`: Application name
|
|
261
|
+
- `metadata.sensorUrl`: Sensor URL for tracking
|
|
262
|
+
- `endActivity(scoreData)`: End activity and submit results
|
|
263
|
+
- `scoreData.correctQuestions`: Number of correct answers
|
|
264
|
+
- `scoreData.totalQuestions`: Total number of questions
|
|
265
|
+
- **XP Query** (`client.timeback.xp`):
|
|
266
|
+
- `today(options?)`: Get today's XP (supports timezone parameter)
|
|
267
|
+
- `total()`: Get total accumulated XP
|
|
268
|
+
- `history(options?)`: Get XP history with optional date filtering
|
|
269
|
+
- `summary(options?)`: Get both today's and total XP in one call
|
|
270
|
+
|
|
231
271
|
#### **Leaderboard** (`client.leaderboard`) - Game-specific
|
|
232
272
|
|
|
233
273
|
- `fetch(options?)`: Get leaderboard for a specific game
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { InferSelectModel } from 'drizzle-orm';
|
|
|
3
3
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
4
4
|
import * as drizzle_zod from 'drizzle-zod';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
+
import * as _playcademy_timeback_types from '@playcademy/timeback/types';
|
|
6
7
|
import { OrganizationConfig, CourseConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
|
|
7
8
|
import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
|
|
8
9
|
|
|
@@ -3022,7 +3023,6 @@ type XpHistoryResponse = {
|
|
|
3022
3023
|
xp: number;
|
|
3023
3024
|
}>;
|
|
3024
3025
|
};
|
|
3025
|
-
type TimebackSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
|
|
3026
3026
|
type TimebackSetupRequest = {
|
|
3027
3027
|
gameId: string;
|
|
3028
3028
|
config: {
|
|
@@ -3106,70 +3106,7 @@ type TimebackVerifyResponse = {
|
|
|
3106
3106
|
};
|
|
3107
3107
|
errors?: string[];
|
|
3108
3108
|
};
|
|
3109
|
-
type
|
|
3110
|
-
gameId: string;
|
|
3111
|
-
studentId: string;
|
|
3112
|
-
progressData: {
|
|
3113
|
-
sensorUrl?: string;
|
|
3114
|
-
subject?: TimebackSubject;
|
|
3115
|
-
appName?: string;
|
|
3116
|
-
score?: number;
|
|
3117
|
-
totalQuestions?: number;
|
|
3118
|
-
correctQuestions?: number;
|
|
3119
|
-
xpEarned?: number;
|
|
3120
|
-
masteredUnits?: number;
|
|
3121
|
-
attemptNumber?: number;
|
|
3122
|
-
activityId?: string;
|
|
3123
|
-
activityName?: string;
|
|
3124
|
-
courseId?: string;
|
|
3125
|
-
classId?: string;
|
|
3126
|
-
courseName?: string;
|
|
3127
|
-
studentEmail?: string;
|
|
3128
|
-
};
|
|
3129
|
-
};
|
|
3130
|
-
type RecordProgressResponse = {
|
|
3131
|
-
status: 'ok';
|
|
3132
|
-
courseId: string;
|
|
3133
|
-
};
|
|
3134
|
-
type RecordSessionEndRequest = {
|
|
3135
|
-
gameId: string;
|
|
3136
|
-
studentId: string;
|
|
3137
|
-
sessionData: {
|
|
3138
|
-
sensorUrl?: string;
|
|
3139
|
-
subject?: TimebackSubject;
|
|
3140
|
-
appName?: string;
|
|
3141
|
-
activeTimeSeconds: number;
|
|
3142
|
-
inactiveTimeSeconds?: number;
|
|
3143
|
-
wasteTimeSeconds?: number;
|
|
3144
|
-
activityId?: string;
|
|
3145
|
-
activityName?: string;
|
|
3146
|
-
courseId?: string;
|
|
3147
|
-
courseName?: string;
|
|
3148
|
-
studentEmail?: string;
|
|
3149
|
-
};
|
|
3150
|
-
};
|
|
3151
|
-
type RecordSessionEndResponse = {
|
|
3152
|
-
status: 'ok';
|
|
3153
|
-
courseId: string;
|
|
3154
|
-
};
|
|
3155
|
-
type AwardXpRequest = {
|
|
3156
|
-
gameId: string;
|
|
3157
|
-
studentId: string;
|
|
3158
|
-
xpAmount: number;
|
|
3159
|
-
metadata: {
|
|
3160
|
-
sensorUrl: string;
|
|
3161
|
-
subject: TimebackSubject;
|
|
3162
|
-
appName: string;
|
|
3163
|
-
reason: string;
|
|
3164
|
-
activityId?: string;
|
|
3165
|
-
activityName?: string;
|
|
3166
|
-
courseId?: string;
|
|
3167
|
-
courseName?: string;
|
|
3168
|
-
studentEmail?: string;
|
|
3169
|
-
bonusType?: string;
|
|
3170
|
-
};
|
|
3171
|
-
};
|
|
3172
|
-
type AwardXpResponse = {
|
|
3109
|
+
type EndActivityResponse = {
|
|
3173
3110
|
status: 'ok';
|
|
3174
3111
|
courseId: string;
|
|
3175
3112
|
xpAwarded: number;
|
|
@@ -4077,9 +4014,10 @@ declare class PlaycademyClient {
|
|
|
4077
4014
|
};
|
|
4078
4015
|
/** TimeBack XP methods (today, total, history) */
|
|
4079
4016
|
timeback: {
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4017
|
+
startActivity: (metadata: _playcademy_timeback_types.ActivityData) => void;
|
|
4018
|
+
pauseActivity: () => void;
|
|
4019
|
+
resumeActivity: () => void;
|
|
4020
|
+
endActivity: (data: _playcademy_timeback_types.EndActivityScoreData) => Promise<EndActivityResponse>;
|
|
4083
4021
|
management: {
|
|
4084
4022
|
setup: (request: TimebackSetupRequest) => Promise<TimebackSetupResponse>;
|
|
4085
4023
|
verify: (gameId: string) => Promise<TimebackVerifyResponse>;
|
package/dist/index.js
CHANGED
|
@@ -110,10 +110,18 @@ var isBrowser = () => {
|
|
|
110
110
|
default:
|
|
111
111
|
return console.log;
|
|
112
112
|
}
|
|
113
|
+
}, levelPriority, getMinimumLogLevel = () => {
|
|
114
|
+
const envLevel = (process.env.LOG_LEVEL ?? "").toLowerCase();
|
|
115
|
+
if (envLevel && ["debug", "info", "warn", "error"].includes(envLevel)) {
|
|
116
|
+
return envLevel;
|
|
117
|
+
}
|
|
118
|
+
return isProduction() ? "info" : "debug";
|
|
119
|
+
}, shouldLog = (level) => {
|
|
120
|
+
const minLevel = getMinimumLogLevel();
|
|
121
|
+
return levelPriority[level] >= levelPriority[minLevel];
|
|
113
122
|
}, performLog = (level, message, context) => {
|
|
114
|
-
if (level
|
|
123
|
+
if (!shouldLog(level))
|
|
115
124
|
return;
|
|
116
|
-
}
|
|
117
125
|
const outputFormat = detectOutputFormat();
|
|
118
126
|
switch (outputFormat) {
|
|
119
127
|
case "browser":
|
|
@@ -148,6 +156,12 @@ var init_src = __esm(() => {
|
|
|
148
156
|
cyan: "\x1B[36m",
|
|
149
157
|
gray: "\x1B[90m"
|
|
150
158
|
};
|
|
159
|
+
levelPriority = {
|
|
160
|
+
debug: 0,
|
|
161
|
+
info: 1,
|
|
162
|
+
warn: 2,
|
|
163
|
+
error: 3
|
|
164
|
+
};
|
|
151
165
|
log = createLogger();
|
|
152
166
|
});
|
|
153
167
|
|
|
@@ -906,9 +920,16 @@ function checkDevWarnings(data) {
|
|
|
906
920
|
if (!warningType)
|
|
907
921
|
return;
|
|
908
922
|
switch (warningType) {
|
|
909
|
-
case "timeback-
|
|
910
|
-
console.warn("%c⚠️ TimeBack
|
|
911
|
-
|
|
923
|
+
case "timeback-not-configured":
|
|
924
|
+
console.warn("%c⚠️ TimeBack Not Configured", "background: #f59e0b; color: white; padding: 6px 12px; border-radius: 4px; font-weight: bold; font-size: 13px");
|
|
925
|
+
console.log("%cTimeBack is configured in playcademy.config.js but the sandbox does not have TimeBack credentials.", "color: #f59e0b; font-weight: 500");
|
|
926
|
+
console.log("To test TimeBack locally:");
|
|
927
|
+
console.log(" Set the following environment variables:");
|
|
928
|
+
console.log(" • %cTIMEBACK_ONEROSTER_API_URL", "color: #0ea5e9; font-weight: 600; font-family: monospace");
|
|
929
|
+
console.log(" • %cTIMEBACK_CALIPER_API_URL", "color: #0ea5e9; font-weight: 600; font-family: monospace");
|
|
930
|
+
console.log(" • %cTIMEBACK_API_CLIENT_ID/SECRET", "color: #0ea5e9; font-weight: 600; font-family: monospace");
|
|
931
|
+
console.log(" Or deploy your game: %cplaycademy deploy", "color: #10b981; font-weight: 600; font-family: monospace");
|
|
932
|
+
console.log(" Or wait for %c@superbuilders/timeback-local%c (coming soon)", "color: #8b5cf6; font-weight: 600; font-family: monospace", "color: inherit");
|
|
912
933
|
break;
|
|
913
934
|
default:
|
|
914
935
|
console.warn(`[Playcademy Dev Warning] ${warningType}`);
|
|
@@ -1548,9 +1569,7 @@ var init_overworld = __esm(() => {
|
|
|
1548
1569
|
var TIMEBACK_ROUTES;
|
|
1549
1570
|
var init_timeback = __esm(() => {
|
|
1550
1571
|
TIMEBACK_ROUTES = {
|
|
1551
|
-
|
|
1552
|
-
SESSION_END: "/integrations/timeback/session-end",
|
|
1553
|
-
AWARD_XP: "/integrations/timeback/award-xp"
|
|
1572
|
+
END_ACTIVITY: "/integrations/timeback/end-activity"
|
|
1554
1573
|
};
|
|
1555
1574
|
});
|
|
1556
1575
|
|
|
@@ -2039,18 +2058,69 @@ var init_achievements = () => {};
|
|
|
2039
2058
|
|
|
2040
2059
|
// src/core/namespaces/timeback.ts
|
|
2041
2060
|
function createTimebackNamespace(client) {
|
|
2061
|
+
let currentActivity = null;
|
|
2042
2062
|
return {
|
|
2043
|
-
|
|
2044
|
-
|
|
2063
|
+
startActivity: (metadata) => {
|
|
2064
|
+
currentActivity = {
|
|
2065
|
+
startTime: Date.now(),
|
|
2066
|
+
metadata,
|
|
2067
|
+
pausedTime: 0,
|
|
2068
|
+
pauseStartTime: null
|
|
2069
|
+
};
|
|
2070
|
+
},
|
|
2071
|
+
pauseActivity: () => {
|
|
2072
|
+
if (!currentActivity) {
|
|
2073
|
+
throw new Error("No activity in progress. Call startActivity() before pauseActivity().");
|
|
2074
|
+
}
|
|
2075
|
+
if (currentActivity.pauseStartTime !== null) {
|
|
2076
|
+
throw new Error("Activity is already paused.");
|
|
2077
|
+
}
|
|
2078
|
+
currentActivity.pauseStartTime = Date.now();
|
|
2045
2079
|
},
|
|
2046
|
-
|
|
2047
|
-
|
|
2080
|
+
resumeActivity: () => {
|
|
2081
|
+
if (!currentActivity) {
|
|
2082
|
+
throw new Error("No activity in progress. Call startActivity() before resumeActivity().");
|
|
2083
|
+
}
|
|
2084
|
+
if (currentActivity.pauseStartTime === null) {
|
|
2085
|
+
throw new Error("Activity is not paused.");
|
|
2086
|
+
}
|
|
2087
|
+
const pauseDuration = Date.now() - currentActivity.pauseStartTime;
|
|
2088
|
+
currentActivity.pausedTime += pauseDuration;
|
|
2089
|
+
currentActivity.pauseStartTime = null;
|
|
2048
2090
|
},
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2091
|
+
endActivity: async (data) => {
|
|
2092
|
+
if (!currentActivity) {
|
|
2093
|
+
throw new Error("No activity in progress. Call startActivity() before endActivity().");
|
|
2094
|
+
}
|
|
2095
|
+
if (currentActivity.pauseStartTime !== null) {
|
|
2096
|
+
const pauseDuration = Date.now() - currentActivity.pauseStartTime;
|
|
2097
|
+
currentActivity.pausedTime += pauseDuration;
|
|
2098
|
+
currentActivity.pauseStartTime = null;
|
|
2099
|
+
}
|
|
2100
|
+
const endTime = Date.now();
|
|
2101
|
+
const totalElapsed = endTime - currentActivity.startTime;
|
|
2102
|
+
const activeTime = totalElapsed - currentActivity.pausedTime;
|
|
2103
|
+
const durationSeconds = Math.floor(activeTime / 1000);
|
|
2104
|
+
const { correctQuestions, totalQuestions } = data;
|
|
2105
|
+
const request2 = {
|
|
2106
|
+
activityData: currentActivity.metadata,
|
|
2107
|
+
scoreData: {
|
|
2108
|
+
correctQuestions,
|
|
2109
|
+
totalQuestions
|
|
2110
|
+
},
|
|
2111
|
+
timingData: {
|
|
2112
|
+
durationSeconds
|
|
2113
|
+
},
|
|
2114
|
+
xpEarned: data.xpAwarded
|
|
2115
|
+
};
|
|
2116
|
+
try {
|
|
2117
|
+
const response = await client["requestGameBackend"](TIMEBACK_ROUTES.END_ACTIVITY, "POST", request2);
|
|
2118
|
+
currentActivity = null;
|
|
2119
|
+
return response;
|
|
2120
|
+
} catch (error) {
|
|
2121
|
+
currentActivity = null;
|
|
2122
|
+
throw error;
|
|
2123
|
+
}
|
|
2054
2124
|
},
|
|
2055
2125
|
management: {
|
|
2056
2126
|
setup: (request2) => {
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _playcademy_timeback_types from '@playcademy/timeback/types';
|
|
2
2
|
import { OrganizationConfig, CourseConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
|
|
3
|
-
export { ComponentConfig, ComponentResourceConfig, CourseConfig,
|
|
3
|
+
export { ActivityData, ComponentConfig, ComponentResourceConfig, CourseConfig, EndActivityPayload, OrganizationConfig, ResourceConfig, TimebackGrade, TimebackSubject } from '@playcademy/timeback/types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @fileoverview Server SDK Type Definitions
|
|
@@ -161,22 +161,14 @@ interface UserInfo {
|
|
|
161
161
|
/** Additional user attributes from the identity provider */
|
|
162
162
|
[key: string]: unknown;
|
|
163
163
|
}
|
|
164
|
-
type
|
|
165
|
-
status: 'ok';
|
|
166
|
-
courseId: string;
|
|
167
|
-
};
|
|
168
|
-
type RecordSessionEndResponse = {
|
|
169
|
-
status: 'ok';
|
|
170
|
-
courseId: string;
|
|
171
|
-
};
|
|
172
|
-
type AwardXpResponse = {
|
|
164
|
+
type EndActivityResponse = {
|
|
173
165
|
status: 'ok';
|
|
174
166
|
courseId: string;
|
|
175
167
|
xpAwarded: number;
|
|
176
168
|
};
|
|
177
169
|
|
|
178
170
|
/**
|
|
179
|
-
* Server-side Playcademy client for recording student
|
|
171
|
+
* Server-side Playcademy client for recording student activity to TimeBack.
|
|
180
172
|
*
|
|
181
173
|
* This client automatically loads game configuration from playcademy.config.js
|
|
182
174
|
* and uses it to auto-fill TimeBack metadata (subject, appName, courseId).
|
|
@@ -188,11 +180,12 @@ type AwardXpResponse = {
|
|
|
188
180
|
* apiKey: process.env.PLAYCADEMY_API_KEY
|
|
189
181
|
* })
|
|
190
182
|
*
|
|
191
|
-
* //
|
|
192
|
-
* await client.timeback.
|
|
193
|
-
*
|
|
194
|
-
* totalQuestions: 20,
|
|
195
|
-
*
|
|
183
|
+
* // End an activity (metadata auto-filled from config)
|
|
184
|
+
* await client.timeback.endActivity(studentId, {
|
|
185
|
+
* activityData: { activityId: 'math-quiz-1' },
|
|
186
|
+
* scoreData: { correctQuestions: 17, totalQuestions: 20, accuracy: 0.85 },
|
|
187
|
+
* timingData: { durationSeconds: 300 },
|
|
188
|
+
* xpEarned: 5
|
|
196
189
|
* })
|
|
197
190
|
* ```
|
|
198
191
|
*/
|
|
@@ -276,12 +269,10 @@ declare class PlaycademyClient {
|
|
|
276
269
|
* @returns The loaded configuration object
|
|
277
270
|
*/
|
|
278
271
|
get config(): PlaycademyServerClientState['config'];
|
|
279
|
-
/** TimeBack integration methods (
|
|
272
|
+
/** TimeBack integration methods (endActivity) */
|
|
280
273
|
timeback: {
|
|
281
274
|
readonly courseId: string | undefined;
|
|
282
|
-
|
|
283
|
-
recordSessionEnd: (studentId: string, sessionData: _playcademy_timeback_types.SessionData) => Promise<RecordSessionEndResponse>;
|
|
284
|
-
awardXP: (studentId: string, xpAmount: number, metadata: _playcademy_timeback_types.XPAwardMetadata) => Promise<AwardXpResponse>;
|
|
275
|
+
endActivity: (studentId: string, payload: _playcademy_timeback_types.EndActivityPayload) => Promise<EndActivityResponse>;
|
|
285
276
|
};
|
|
286
277
|
}
|
|
287
278
|
|
package/dist/server.js
CHANGED
|
@@ -27,30 +27,11 @@ function createTimebackNamespace(client) {
|
|
|
27
27
|
throw new Error(`Failed to fetch courseId: ${error instanceof Error ? error.message : String(error)}`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function enrichActivityData(data) {
|
|
31
31
|
return {
|
|
32
32
|
...data,
|
|
33
33
|
subject: data.subject || client.config.integrations?.timeback?.course.subjects?.[0],
|
|
34
34
|
appName: data.appName || client.config.name,
|
|
35
|
-
activityName: data.activityName || client.config.name,
|
|
36
|
-
courseName: data.courseName || client.config.integrations?.timeback?.course.title
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function enrichSessionData(data) {
|
|
40
|
-
return {
|
|
41
|
-
...data,
|
|
42
|
-
subject: data.subject || client.config.integrations?.timeback?.course.subjects?.[0],
|
|
43
|
-
appName: data.appName || client.config.name,
|
|
44
|
-
activityName: data.activityName || client.config.name,
|
|
45
|
-
courseName: data.courseName || client.config.integrations?.timeback?.course.title
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function enrichXPMetadata(data) {
|
|
49
|
-
return {
|
|
50
|
-
...data,
|
|
51
|
-
subject: data.subject || client.config.integrations?.timeback?.course.subjects?.[0],
|
|
52
|
-
appName: data.appName || client.config.name,
|
|
53
|
-
activityName: data.activityName || client.config.name,
|
|
54
35
|
courseName: data.courseName || client.config.integrations?.timeback?.course.title
|
|
55
36
|
};
|
|
56
37
|
}
|
|
@@ -58,35 +39,16 @@ function createTimebackNamespace(client) {
|
|
|
58
39
|
get courseId() {
|
|
59
40
|
return courseId;
|
|
60
41
|
},
|
|
61
|
-
|
|
42
|
+
endActivity: async (studentId, payload) => {
|
|
62
43
|
await ensureCourseId();
|
|
63
|
-
const
|
|
64
|
-
return client["request"]("/api/timeback/
|
|
44
|
+
const enrichedActivityData = enrichActivityData(payload.activityData);
|
|
45
|
+
return client["request"]("/api/timeback/end-activity", "POST", {
|
|
65
46
|
gameId: client.gameId,
|
|
66
47
|
studentId,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await ensureCourseId();
|
|
72
|
-
const enrichedData = enrichSessionData(sessionData);
|
|
73
|
-
return client["request"]("/api/timeback/session-end", "POST", {
|
|
74
|
-
gameId: client.gameId,
|
|
75
|
-
studentId,
|
|
76
|
-
sessionData: enrichedData
|
|
77
|
-
});
|
|
78
|
-
},
|
|
79
|
-
awardXP: async (studentId, xpAmount, metadata) => {
|
|
80
|
-
await ensureCourseId();
|
|
81
|
-
if (typeof xpAmount !== "number" || xpAmount <= 0) {
|
|
82
|
-
throw new Error("[Playcademy SDK] xpAmount must be a positive number");
|
|
83
|
-
}
|
|
84
|
-
const enrichedMetadata = enrichXPMetadata(metadata);
|
|
85
|
-
return client["request"]("/api/timeback/award-xp", "POST", {
|
|
86
|
-
gameId: client.gameId,
|
|
87
|
-
studentId,
|
|
88
|
-
xpAmount,
|
|
89
|
-
metadata: enrichedMetadata
|
|
48
|
+
activityData: enrichedActivityData,
|
|
49
|
+
scoreData: payload.scoreData,
|
|
50
|
+
timingData: payload.timingData,
|
|
51
|
+
xpEarned: payload.xpEarned
|
|
90
52
|
});
|
|
91
53
|
}
|
|
92
54
|
};
|
|
@@ -348,8 +310,14 @@ Please set the PLAYCADEMY_BASE_URL environment variable`);
|
|
|
348
310
|
body: JSON.stringify({ token: gameToken })
|
|
349
311
|
});
|
|
350
312
|
if (!response.ok) {
|
|
351
|
-
|
|
352
|
-
|
|
313
|
+
let errorMessage = "Unknown error";
|
|
314
|
+
try {
|
|
315
|
+
const data = await response.json();
|
|
316
|
+
errorMessage = data.error || data.message || "Unknown error";
|
|
317
|
+
} catch {
|
|
318
|
+
errorMessage = response.statusText || "Unknown error";
|
|
319
|
+
}
|
|
320
|
+
throw new Error(`[Playcademy SDK] Token verification failed: ${response.status} ${errorMessage}`);
|
|
353
321
|
}
|
|
354
322
|
const result = await response.json();
|
|
355
323
|
return result;
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
|
4
4
|
import * as drizzle_zod from 'drizzle-zod';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import * as _playcademy_realtime_server_types from '@playcademy/realtime/server/types';
|
|
7
|
+
import * as _playcademy_timeback_types from '@playcademy/timeback/types';
|
|
7
8
|
import { OrganizationConfig, CourseConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
|
|
8
9
|
|
|
9
10
|
declare const userRoleEnum: drizzle_orm_pg_core.PgEnum<["admin", "player", "developer"]>;
|
|
@@ -3706,7 +3707,6 @@ type XpHistoryResponse = {
|
|
|
3706
3707
|
xp: number;
|
|
3707
3708
|
}>;
|
|
3708
3709
|
};
|
|
3709
|
-
type TimebackSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
|
|
3710
3710
|
type TimebackSetupRequest = {
|
|
3711
3711
|
gameId: string;
|
|
3712
3712
|
config: {
|
|
@@ -3790,70 +3790,7 @@ type TimebackVerifyResponse = {
|
|
|
3790
3790
|
};
|
|
3791
3791
|
errors?: string[];
|
|
3792
3792
|
};
|
|
3793
|
-
type
|
|
3794
|
-
gameId: string;
|
|
3795
|
-
studentId: string;
|
|
3796
|
-
progressData: {
|
|
3797
|
-
sensorUrl?: string;
|
|
3798
|
-
subject?: TimebackSubject;
|
|
3799
|
-
appName?: string;
|
|
3800
|
-
score?: number;
|
|
3801
|
-
totalQuestions?: number;
|
|
3802
|
-
correctQuestions?: number;
|
|
3803
|
-
xpEarned?: number;
|
|
3804
|
-
masteredUnits?: number;
|
|
3805
|
-
attemptNumber?: number;
|
|
3806
|
-
activityId?: string;
|
|
3807
|
-
activityName?: string;
|
|
3808
|
-
courseId?: string;
|
|
3809
|
-
classId?: string;
|
|
3810
|
-
courseName?: string;
|
|
3811
|
-
studentEmail?: string;
|
|
3812
|
-
};
|
|
3813
|
-
};
|
|
3814
|
-
type RecordProgressResponse = {
|
|
3815
|
-
status: 'ok';
|
|
3816
|
-
courseId: string;
|
|
3817
|
-
};
|
|
3818
|
-
type RecordSessionEndRequest = {
|
|
3819
|
-
gameId: string;
|
|
3820
|
-
studentId: string;
|
|
3821
|
-
sessionData: {
|
|
3822
|
-
sensorUrl?: string;
|
|
3823
|
-
subject?: TimebackSubject;
|
|
3824
|
-
appName?: string;
|
|
3825
|
-
activeTimeSeconds: number;
|
|
3826
|
-
inactiveTimeSeconds?: number;
|
|
3827
|
-
wasteTimeSeconds?: number;
|
|
3828
|
-
activityId?: string;
|
|
3829
|
-
activityName?: string;
|
|
3830
|
-
courseId?: string;
|
|
3831
|
-
courseName?: string;
|
|
3832
|
-
studentEmail?: string;
|
|
3833
|
-
};
|
|
3834
|
-
};
|
|
3835
|
-
type RecordSessionEndResponse = {
|
|
3836
|
-
status: 'ok';
|
|
3837
|
-
courseId: string;
|
|
3838
|
-
};
|
|
3839
|
-
type AwardXpRequest = {
|
|
3840
|
-
gameId: string;
|
|
3841
|
-
studentId: string;
|
|
3842
|
-
xpAmount: number;
|
|
3843
|
-
metadata: {
|
|
3844
|
-
sensorUrl: string;
|
|
3845
|
-
subject: TimebackSubject;
|
|
3846
|
-
appName: string;
|
|
3847
|
-
reason: string;
|
|
3848
|
-
activityId?: string;
|
|
3849
|
-
activityName?: string;
|
|
3850
|
-
courseId?: string;
|
|
3851
|
-
courseName?: string;
|
|
3852
|
-
studentEmail?: string;
|
|
3853
|
-
bonusType?: string;
|
|
3854
|
-
};
|
|
3855
|
-
};
|
|
3856
|
-
type AwardXpResponse = {
|
|
3793
|
+
type EndActivityResponse = {
|
|
3857
3794
|
status: 'ok';
|
|
3858
3795
|
courseId: string;
|
|
3859
3796
|
xpAwarded: number;
|
|
@@ -4765,9 +4702,10 @@ declare class PlaycademyClient {
|
|
|
4765
4702
|
};
|
|
4766
4703
|
/** TimeBack XP methods (today, total, history) */
|
|
4767
4704
|
timeback: {
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4705
|
+
startActivity: (metadata: _playcademy_timeback_types.ActivityData) => void;
|
|
4706
|
+
pauseActivity: () => void;
|
|
4707
|
+
resumeActivity: () => void;
|
|
4708
|
+
endActivity: (data: _playcademy_timeback_types.EndActivityScoreData) => Promise<EndActivityResponse>;
|
|
4771
4709
|
management: {
|
|
4772
4710
|
setup: (request: TimebackSetupRequest) => Promise<TimebackSetupResponse>;
|
|
4773
4711
|
verify: (gameId: string) => Promise<TimebackVerifyResponse>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcademy/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@playcademy/timeback": "0.0.1",
|
|
43
43
|
"@playcademy/utils": "0.0.1",
|
|
44
44
|
"@types/bun": "latest",
|
|
45
|
-
"playcademy": "0.11.
|
|
45
|
+
"playcademy": "0.11.4",
|
|
46
46
|
"rollup": "^4.50.2",
|
|
47
47
|
"rollup-plugin-dts": "^6.2.3",
|
|
48
48
|
"typescript": "^5.7.2",
|