@playcademy/sdk 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +300 -339
- package/dist/index.js +186 -29
- package/dist/internal.d.ts +2989 -2224
- package/dist/internal.js +218 -64
- package/dist/server.d.ts +298 -39
- package/dist/server.js +104 -8
- package/dist/types.d.ts +2134 -563
- package/package.json +4 -2
package/dist/server.d.ts
CHANGED
|
@@ -1,43 +1,292 @@
|
|
|
1
1
|
import { SchemaInfo } from '@playcademy/cloudflare';
|
|
2
|
-
import * as _playcademy_timeback_types from '@playcademy/timeback/types';
|
|
3
|
-
import { CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
|
|
4
|
-
export { ActivityData, ComponentConfig, ComponentResourceConfig, EndActivityPayload, OrganizationConfig, ResourceConfig, TimebackGrade, TimebackSubject } from '@playcademy/timeback/types';
|
|
5
2
|
|
|
6
3
|
/**
|
|
7
|
-
*
|
|
4
|
+
* TimeBack Enums & Literal Types
|
|
5
|
+
*
|
|
6
|
+
* Basic type definitions used throughout the TimeBack integration.
|
|
7
|
+
*
|
|
8
|
+
* @module types/timeback/types
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Valid TimeBack subject values for course configuration.
|
|
12
|
+
* These are the supported subject values for OneRoster courses.
|
|
13
|
+
*/
|
|
14
|
+
type TimebackSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
|
|
15
|
+
/**
|
|
16
|
+
* Grade levels per AE OneRoster GradeEnum.
|
|
17
|
+
* -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
|
|
18
|
+
*/
|
|
19
|
+
type TimebackGrade = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
|
|
20
|
+
/**
|
|
21
|
+
* Valid Caliper subject values.
|
|
22
|
+
* Matches OneRoster subjects, with "None" as a Caliper-specific fallback.
|
|
23
|
+
*/
|
|
24
|
+
type CaliperSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
|
|
25
|
+
/**
|
|
26
|
+
* OneRoster organization types.
|
|
27
|
+
*/
|
|
28
|
+
type OrganizationType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
|
|
29
|
+
/**
|
|
30
|
+
* Lesson types for PowerPath integration.
|
|
31
|
+
*/
|
|
32
|
+
type LessonType = 'powerpath-100' | 'quiz' | 'test-out' | 'placement' | 'unit-test' | 'alpha-read-article' | null;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* TimeBack Configuration Types
|
|
36
|
+
*
|
|
37
|
+
* Configuration interfaces for Organization, Course, Component,
|
|
38
|
+
* Resource, and complete TimeBack setup.
|
|
39
|
+
*
|
|
40
|
+
* @module types/timeback/config
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Organization configuration for TimeBack (user input - optionals allowed)
|
|
45
|
+
*/
|
|
46
|
+
interface OrganizationConfig {
|
|
47
|
+
/** Display name for your organization */
|
|
48
|
+
name?: string;
|
|
49
|
+
/** Organization type */
|
|
50
|
+
type?: OrganizationType;
|
|
51
|
+
/** Unique identifier (defaults to Playcademy's org) */
|
|
52
|
+
identifier?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Course goals for daily student targets
|
|
56
|
+
*/
|
|
57
|
+
interface CourseGoals {
|
|
58
|
+
/** Target XP students should earn per day */
|
|
59
|
+
dailyXp?: number;
|
|
60
|
+
/** Target lessons per day */
|
|
61
|
+
dailyLessons?: number;
|
|
62
|
+
/** Target active minutes per day */
|
|
63
|
+
dailyActiveMinutes?: number;
|
|
64
|
+
/** Target accuracy percentage */
|
|
65
|
+
dailyAccuracy?: number;
|
|
66
|
+
/** Target mastered units per day */
|
|
67
|
+
dailyMasteredUnits?: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Course metrics and totals
|
|
71
|
+
*/
|
|
72
|
+
interface CourseMetrics {
|
|
73
|
+
/** Total XP available in the course */
|
|
74
|
+
totalXp?: number;
|
|
75
|
+
/** Total lessons/activities in the course */
|
|
76
|
+
totalLessons?: number;
|
|
77
|
+
/** Total number of grade levels covered by this course */
|
|
78
|
+
totalGrades?: number;
|
|
79
|
+
/** The type of course (e.g. 'optional', 'hole-filling', 'base') */
|
|
80
|
+
courseType?: 'base' | 'hole-filling' | 'optional' | 'Base' | 'Hole-Filling' | 'Optional';
|
|
81
|
+
/** Indicates whether the course is supplemental content */
|
|
82
|
+
isSupplemental?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Complete course metadata structure
|
|
86
|
+
*/
|
|
87
|
+
interface CourseMetadata {
|
|
88
|
+
/** Define the type of course and priority for the student */
|
|
89
|
+
courseType?: 'base' | 'hole-filling' | 'optional';
|
|
90
|
+
/** Boolean value to determine if a course is supplemental to a base course */
|
|
91
|
+
isSupplemental?: boolean;
|
|
92
|
+
/** Boolean value to determine if a course is custom to an individual student */
|
|
93
|
+
isCustom?: boolean;
|
|
94
|
+
/** Signals whether a course is in production with students */
|
|
95
|
+
publishStatus?: 'draft' | 'testing' | 'published' | 'deactivated';
|
|
96
|
+
/** Who to contact when issues reported with questions */
|
|
97
|
+
contactEmail?: string;
|
|
98
|
+
/** Primary app identifier */
|
|
99
|
+
primaryApp?: string;
|
|
100
|
+
/** Learning goals for students */
|
|
101
|
+
goals?: CourseGoals;
|
|
102
|
+
/** Course metrics and totals */
|
|
103
|
+
metrics?: CourseMetrics;
|
|
104
|
+
/** Vendor-specific metadata (e.g., AlphaLearn) */
|
|
25
105
|
[key: string]: unknown;
|
|
26
106
|
}
|
|
27
107
|
/**
|
|
28
|
-
*
|
|
108
|
+
* Course configuration for TimeBack (user input)
|
|
109
|
+
*/
|
|
110
|
+
interface CourseConfig {
|
|
111
|
+
/** Allocated OneRoster sourcedId (set after creation) */
|
|
112
|
+
sourcedId?: string;
|
|
113
|
+
/** Course title (defaults to game name) */
|
|
114
|
+
title?: string;
|
|
115
|
+
/** Subjects (REQUIRED for TimeBack integration) */
|
|
116
|
+
subjects: TimebackSubject[];
|
|
117
|
+
/** Used when recording progress/sessions if not explicitly specified per event. */
|
|
118
|
+
defaultSubject?: TimebackSubject;
|
|
119
|
+
/** Grade levels (REQUIRED for TimeBack integration) */
|
|
120
|
+
grades: TimebackGrade[];
|
|
121
|
+
/** Short course code (optional, auto-generated) */
|
|
122
|
+
courseCode?: string;
|
|
123
|
+
/** Course level (auto-derived from grades) */
|
|
124
|
+
level?: 'Elementary' | 'Middle' | 'High' | 'AP' | string;
|
|
125
|
+
/** Grading system */
|
|
126
|
+
gradingScheme?: 'STANDARD';
|
|
127
|
+
/** Total XP available in this course (REQUIRED before setup) */
|
|
128
|
+
totalXp?: number | null;
|
|
129
|
+
/** Total masterable units in this course (REQUIRED before setup) */
|
|
130
|
+
masterableUnits?: number | null;
|
|
131
|
+
/** Custom Playcademy metadata */
|
|
132
|
+
metadata?: CourseMetadata;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Component configuration for TimeBack (user input)
|
|
136
|
+
*/
|
|
137
|
+
interface ComponentConfig {
|
|
138
|
+
/** Component title (defaults to "{course.title} Activities") */
|
|
139
|
+
title?: string;
|
|
140
|
+
/** Display order */
|
|
141
|
+
sortOrder?: number;
|
|
142
|
+
/** Required prior components */
|
|
143
|
+
prerequisites?: string[];
|
|
144
|
+
/** How prerequisites work */
|
|
145
|
+
prerequisiteCriteria?: 'ALL' | 'ANY';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Playcademy-specific resource extensions
|
|
149
|
+
*/
|
|
150
|
+
interface PlaycademyResourceMetadata {
|
|
151
|
+
/** Mastery configuration for tracking discrete learning units */
|
|
152
|
+
mastery?: {
|
|
153
|
+
/** Total number of masterable units in the resource */
|
|
154
|
+
masterableUnits: number;
|
|
155
|
+
/** Type of mastery unit for semantic clarity */
|
|
156
|
+
unitType?: 'level' | 'rank' | 'skill' | 'module';
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Resource configuration for TimeBack (user input)
|
|
161
|
+
*/
|
|
162
|
+
interface ResourceConfig {
|
|
163
|
+
/** Resource title (defaults to "{course.title} Game") */
|
|
164
|
+
title?: string;
|
|
165
|
+
/** Internal resource ID (auto-generated from package.json) */
|
|
166
|
+
vendorResourceId?: string;
|
|
167
|
+
/** Vendor identifier */
|
|
168
|
+
vendorId?: string;
|
|
169
|
+
/** Application identifier */
|
|
170
|
+
applicationId?: string;
|
|
171
|
+
/** Resource roles */
|
|
172
|
+
roles?: ('primary' | 'secondary')[];
|
|
173
|
+
/** Resource importance */
|
|
174
|
+
importance?: 'primary' | 'secondary';
|
|
175
|
+
/** Interactive resource metadata */
|
|
176
|
+
metadata?: {
|
|
177
|
+
/** Resource type */
|
|
178
|
+
type?: 'interactive';
|
|
179
|
+
/** Launch URL (defaults to Playcademy game URL) */
|
|
180
|
+
launchUrl?: string;
|
|
181
|
+
/** Platform name */
|
|
182
|
+
toolProvider?: string;
|
|
183
|
+
/** Teaching method */
|
|
184
|
+
instructionalMethod?: 'exploratory' | 'direct-instruction';
|
|
185
|
+
/** Subject area */
|
|
186
|
+
subject?: TimebackSubject;
|
|
187
|
+
/** Target grades */
|
|
188
|
+
grades?: TimebackGrade[];
|
|
189
|
+
/** Content language */
|
|
190
|
+
language?: string;
|
|
191
|
+
/** Base XP for completion */
|
|
192
|
+
xp?: number;
|
|
193
|
+
/** Playcademy-specific extensions */
|
|
194
|
+
playcademy?: PlaycademyResourceMetadata;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Component Resource link configuration (user input)
|
|
199
|
+
*/
|
|
200
|
+
interface ComponentResourceConfig {
|
|
201
|
+
/** Link title (defaults to "{resource.title} Activity") */
|
|
202
|
+
title?: string;
|
|
203
|
+
/** Display order */
|
|
204
|
+
sortOrder?: number;
|
|
205
|
+
/** Lesson type for PowerPath integration */
|
|
206
|
+
lessonType?: LessonType;
|
|
207
|
+
}
|
|
208
|
+
interface TimebackCourseConfig {
|
|
209
|
+
subject: string;
|
|
210
|
+
grade: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* TimeBack Client SDK DTOs
|
|
29
215
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* This base type only includes the minimal required fields.
|
|
216
|
+
* Data transfer objects for the TimeBack client SDK including
|
|
217
|
+
* progress tracking, session management, and activity completion.
|
|
33
218
|
*
|
|
34
|
-
*
|
|
219
|
+
* Note: TimebackClientConfig lives in @playcademy/timeback as it's
|
|
220
|
+
* SDK configuration, not a DTO.
|
|
221
|
+
*
|
|
222
|
+
* @module types/timeback/client
|
|
35
223
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Activity data for ending an activity
|
|
227
|
+
*/
|
|
228
|
+
interface ActivityData {
|
|
229
|
+
/** Unique activity identifier (required) */
|
|
230
|
+
activityId: string;
|
|
231
|
+
/** Grade level for this activity (required for multi-grade course routing) */
|
|
38
232
|
grade: number;
|
|
39
|
-
|
|
40
|
-
|
|
233
|
+
/** Subject area (required for multi-grade course routing) */
|
|
234
|
+
subject: CaliperSubject;
|
|
235
|
+
/** Activity display name (optional) */
|
|
236
|
+
activityName?: string;
|
|
237
|
+
/** Course identifier (auto-filled from config if not provided) */
|
|
238
|
+
courseId?: string;
|
|
239
|
+
/** Course display name (auto-filled from config if not provided) */
|
|
240
|
+
courseName?: string;
|
|
241
|
+
/** Student email address (optional) */
|
|
242
|
+
studentEmail?: string;
|
|
243
|
+
/** Application name for Caliper events (defaults to 'Game') */
|
|
244
|
+
appName?: string;
|
|
245
|
+
/** Sensor URL for Caliper events (defaults to baseUrl) */
|
|
246
|
+
sensorUrl?: string;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Score data for activity completion
|
|
250
|
+
*/
|
|
251
|
+
interface ScoreData {
|
|
252
|
+
/** Number of questions answered correctly */
|
|
253
|
+
correctQuestions: number;
|
|
254
|
+
/** Total number of questions */
|
|
255
|
+
totalQuestions: number;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Timing data for activity completion
|
|
259
|
+
*/
|
|
260
|
+
interface TimingData {
|
|
261
|
+
/** Duration of the activity in seconds */
|
|
262
|
+
durationSeconds: number;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Complete payload for ending an activity
|
|
266
|
+
*/
|
|
267
|
+
interface EndActivityPayload {
|
|
268
|
+
/** Activity metadata */
|
|
269
|
+
activityData: ActivityData;
|
|
270
|
+
/** Score information */
|
|
271
|
+
scoreData: ScoreData;
|
|
272
|
+
/** Timing information */
|
|
273
|
+
timingData: TimingData;
|
|
274
|
+
/** Explicit XP value to override automatic calculation */
|
|
275
|
+
xpEarned?: number;
|
|
276
|
+
/** Number of learning units mastered */
|
|
277
|
+
masteredUnits?: number;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* TimeBack API Request/Response Types
|
|
282
|
+
*
|
|
283
|
+
* Types for TimeBack API endpoints including XP tracking,
|
|
284
|
+
* setup, verification, and activity completion.
|
|
285
|
+
*
|
|
286
|
+
* @module types/timeback/api
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
interface EndActivityResponse {
|
|
41
290
|
status: 'ok';
|
|
42
291
|
courseId: string;
|
|
43
292
|
xpAwarded: number;
|
|
@@ -45,7 +294,7 @@ type EndActivityResponse = {
|
|
|
45
294
|
pctCompleteApp?: number;
|
|
46
295
|
scoreStatus?: string;
|
|
47
296
|
inProgress?: string;
|
|
48
|
-
}
|
|
297
|
+
}
|
|
49
298
|
|
|
50
299
|
/**
|
|
51
300
|
* @fileoverview Server SDK Type Definitions
|
|
@@ -253,6 +502,23 @@ interface BackendDeploymentBundle {
|
|
|
253
502
|
secrets?: Record<string, string>;
|
|
254
503
|
}
|
|
255
504
|
|
|
505
|
+
/**
|
|
506
|
+
* OpenID Connect UserInfo claims (NOT a database row).
|
|
507
|
+
*/
|
|
508
|
+
interface UserInfo {
|
|
509
|
+
sub: string;
|
|
510
|
+
email: string;
|
|
511
|
+
name: string | null;
|
|
512
|
+
email_verified?: boolean;
|
|
513
|
+
given_name?: string;
|
|
514
|
+
family_name?: string;
|
|
515
|
+
issuer?: string;
|
|
516
|
+
lti_roles?: unknown;
|
|
517
|
+
lti_context?: unknown;
|
|
518
|
+
lti_resource_link?: unknown;
|
|
519
|
+
timeback_id?: string;
|
|
520
|
+
}
|
|
521
|
+
|
|
256
522
|
/**
|
|
257
523
|
* Server-side Playcademy client for recording student activity to TimeBack.
|
|
258
524
|
*
|
|
@@ -322,13 +588,6 @@ declare class PlaycademyClient {
|
|
|
322
588
|
* ```
|
|
323
589
|
*/
|
|
324
590
|
static init(config: PlaycademyServerClientConfig): Promise<PlaycademyClient>;
|
|
325
|
-
/**
|
|
326
|
-
* Fetch gameId from API using the API token.
|
|
327
|
-
*
|
|
328
|
-
* @private
|
|
329
|
-
* @throws {Error} Always throws - gameId fetching not yet implemented
|
|
330
|
-
* @todo Implement API endpoint to fetch gameId from API token
|
|
331
|
-
*/
|
|
332
591
|
private fetchGameId;
|
|
333
592
|
/**
|
|
334
593
|
* Makes an authenticated HTTP request to the API.
|
|
@@ -357,7 +616,7 @@ declare class PlaycademyClient {
|
|
|
357
616
|
get config(): PlaycademyServerClientState['config'];
|
|
358
617
|
/** TimeBack integration methods (endActivity) */
|
|
359
618
|
timeback: {
|
|
360
|
-
endActivity: (studentId: string, payload:
|
|
619
|
+
endActivity: (studentId: string, payload: EndActivityPayload) => Promise<EndActivityResponse>;
|
|
361
620
|
};
|
|
362
621
|
}
|
|
363
622
|
|
|
@@ -420,4 +679,4 @@ declare function verifyGameToken(gameToken: string, options?: {
|
|
|
420
679
|
}): Promise<VerifyGameTokenResponse>;
|
|
421
680
|
|
|
422
681
|
export { PlaycademyClient, verifyGameToken };
|
|
423
|
-
export type { BackendDeploymentBundle, BackendResourceBindings, IntegrationsConfig, PlaycademyConfig, PlaycademyServerClientConfig, PlaycademyServerClientState, TimebackBaseConfig, TimebackCourseConfigWithOverrides, TimebackIntegrationConfig, UserInfo };
|
|
682
|
+
export type { ActivityData, BackendDeploymentBundle, BackendResourceBindings, ComponentConfig, ComponentResourceConfig, EndActivityPayload, IntegrationsConfig, OrganizationConfig, PlaycademyConfig, PlaycademyServerClientConfig, PlaycademyServerClientState, ResourceConfig, TimebackBaseConfig, TimebackCourseConfigWithOverrides, TimebackGrade, TimebackIntegrationConfig, TimebackSubject, UserInfo };
|
package/dist/server.js
CHANGED
|
@@ -27,6 +27,99 @@ function createTimebackNamespace(client) {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
// src/core/errors.ts
|
|
31
|
+
class PlaycademyError extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "PlaycademyError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class ApiError extends Error {
|
|
39
|
+
status;
|
|
40
|
+
code;
|
|
41
|
+
details;
|
|
42
|
+
rawBody;
|
|
43
|
+
constructor(status, code, message, details, rawBody) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.status = status;
|
|
46
|
+
this.name = "ApiError";
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.details = details;
|
|
49
|
+
this.rawBody = rawBody;
|
|
50
|
+
Object.setPrototypeOf(this, ApiError.prototype);
|
|
51
|
+
}
|
|
52
|
+
static fromResponse(status, statusText, body) {
|
|
53
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
54
|
+
const errorBody = body;
|
|
55
|
+
const err = errorBody.error;
|
|
56
|
+
if (err && typeof err === "object") {
|
|
57
|
+
return new ApiError(status, err.code ?? statusCodeToErrorCode(status), err.message ?? statusText, err.details, body);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return new ApiError(status, statusCodeToErrorCode(status), statusText, undefined, body);
|
|
61
|
+
}
|
|
62
|
+
is(code) {
|
|
63
|
+
return this.code === code;
|
|
64
|
+
}
|
|
65
|
+
isClientError() {
|
|
66
|
+
return this.status >= 400 && this.status < 500;
|
|
67
|
+
}
|
|
68
|
+
isServerError() {
|
|
69
|
+
return this.status >= 500;
|
|
70
|
+
}
|
|
71
|
+
isRetryable() {
|
|
72
|
+
return this.isServerError() || this.code === "TOO_MANY_REQUESTS" || this.code === "RATE_LIMITED" || this.code === "TIMEOUT";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function statusCodeToErrorCode(status) {
|
|
76
|
+
switch (status) {
|
|
77
|
+
case 400:
|
|
78
|
+
return "BAD_REQUEST";
|
|
79
|
+
case 401:
|
|
80
|
+
return "UNAUTHORIZED";
|
|
81
|
+
case 403:
|
|
82
|
+
return "FORBIDDEN";
|
|
83
|
+
case 404:
|
|
84
|
+
return "NOT_FOUND";
|
|
85
|
+
case 405:
|
|
86
|
+
return "METHOD_NOT_ALLOWED";
|
|
87
|
+
case 409:
|
|
88
|
+
return "CONFLICT";
|
|
89
|
+
case 410:
|
|
90
|
+
return "GONE";
|
|
91
|
+
case 412:
|
|
92
|
+
return "PRECONDITION_FAILED";
|
|
93
|
+
case 413:
|
|
94
|
+
return "PAYLOAD_TOO_LARGE";
|
|
95
|
+
case 422:
|
|
96
|
+
return "VALIDATION_FAILED";
|
|
97
|
+
case 429:
|
|
98
|
+
return "TOO_MANY_REQUESTS";
|
|
99
|
+
case 500:
|
|
100
|
+
return "INTERNAL_ERROR";
|
|
101
|
+
case 501:
|
|
102
|
+
return "NOT_IMPLEMENTED";
|
|
103
|
+
case 503:
|
|
104
|
+
return "SERVICE_UNAVAILABLE";
|
|
105
|
+
case 504:
|
|
106
|
+
return "TIMEOUT";
|
|
107
|
+
default:
|
|
108
|
+
return status >= 500 ? "INTERNAL_ERROR" : "BAD_REQUEST";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function extractApiErrorInfo(error) {
|
|
112
|
+
if (!(error instanceof ApiError)) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
status: error.status,
|
|
117
|
+
code: error.code,
|
|
118
|
+
message: error.message,
|
|
119
|
+
...error.details !== undefined && { details: error.details }
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
30
123
|
// src/server/request.ts
|
|
31
124
|
async function makeApiRequest(baseUrl, apiToken, endpoint, method = "GET", body) {
|
|
32
125
|
const url = `${baseUrl}${endpoint}`;
|
|
@@ -40,16 +133,19 @@ async function makeApiRequest(baseUrl, apiToken, endpoint, method = "GET", body)
|
|
|
40
133
|
if (body && (method === "POST" || method === "PUT")) {
|
|
41
134
|
options.body = JSON.stringify(body);
|
|
42
135
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
136
|
+
const response = await fetch(url, options);
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
let errorBody;
|
|
139
|
+
try {
|
|
140
|
+
errorBody = await response.json();
|
|
141
|
+
} catch {
|
|
142
|
+
try {
|
|
143
|
+
errorBody = await response.text();
|
|
144
|
+
} catch {}
|
|
48
145
|
}
|
|
49
|
-
|
|
50
|
-
} catch (error) {
|
|
51
|
-
throw new Error(`Request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
146
|
+
throw ApiError.fromResponse(response.status, response.statusText, errorBody);
|
|
52
147
|
}
|
|
148
|
+
return await response.json();
|
|
53
149
|
}
|
|
54
150
|
|
|
55
151
|
// src/server/utils/config-loader.ts
|